pg_partman を使用した大規模時系列データ向けの PostgreSQL パーティショニングの自動化
Lukas Schneider
DevOps Engineer · Leapcell

はじめに
ビッグデータの時代において、特に時系列情報を扱う場合、増え続けるデータセットの管理は重要な課題です。IoT センサーの読み取り値、金融取引ログ、ネットワークテレメトリデータなどを想像してみてください。これらのデータセットは、すぐにギガバイト、テラバイト、さらにはペタバイトへと急増する可能性があります。データ量が増加するにつれて、これらのテーブルのクエリ、アーカイブ、メンテナンスの複雑さも増していきます。単一のモノリシックテーブルにデータを単に追加するだけでは、パフォーマンスの低下、vacuum のオーバーヘッドの増加、インデックスの再構築の長期化につながることは避けられません。ここでデータベースパーティショニングが登場します。これは、特定の基準(多くの場合、時間)に基づいて、大きなテーブルをより小さく、より管理しやすい断片に分割する戦略を提供するものです。PostgreSQL はネイティブのパーティショニング機能を提供していますが、数千のパーティション(作成、削除、インデックス作成を含む)を手動で管理することは、管理上の悪夢となります。この記事では、このプロセスを自動化するために設計された強力な拡張機能である pg_partman について詳しく説明し、PostgreSQL で超大規模な時系列データテーブルの管理を効率的かつ実質的に実行可能にします。
時系列データ管理の理解
pg_partman に深く入る前に、その有用性を理解するために不可欠ないくつかのコアコンセプトを明確にしましょう。
- 時系列データ: 時間によってインデックス付けされたデータポイントで、通常は一定期間にわたって収集されます。その一般的な特性には、高い挿入レート、追加専用の性質(またはまれな更新)、および通常は時間範囲でフィルタリングされるクエリが含まれます。
- データベースパーティショニング: 大きな論理テーブルをパーティションと呼ばれる小さな物理的な断片に分割する技術です。PostgreSQL では、これは通常
RANGEまたはLISTパーティショニングを使用して行われます。時系列データの場合、タイムスタンプ列に対するRANGEパーティショニングが最も一般的なアプローチであり、特定の時間間隔(例: 日次、週次、月次)のパーティションが作成されます。 - 宣言的パーティショニング: PostgreSQL 10 以降、
PARTITION BY RANGEまたはPARTITION BY LISTを使用してCREATE TABLEステートメントで直接パーティショニングを定義できます。子パーティションはCREATE TABLE ... PARTITION OF ...で作成されます。 - パーティションプルーニング: クエリプランナーがクエリの
WHERE句を調べ、関連データが含まれていないパーティションを削除(プルーニング)する最適化であり、スキャンされるデータ量を大幅に削減します。 - 保持ポリシー: データの保持期間を定義するルールのセットです。時系列データの場合、古いデータは関連性が低くなることがよくあり、より安価なストレージに移動する必要があるため、堅牢なアーカイブとパージメカニズムが必要になります。
pg_partman は、これらの宣言的パーティションのライフサイクル管理を自動化するギャップに対処します。PostgreSQL はビルディングブロックを提供しますが、pg_partman はパーティションの作成、期限切れ、インデックス作成をオーケストレーションし、管理者を退屈な手作業から解放します。
pg_partman の仕組み
pg_partman は、管理対象の各親テーブルのパーティショニングスキームを定義するメタデータテーブル(partman.part_config)を維持することで機能します。この構成に基づいて、指定された保持ポリシーに従って動的に将来のパーティションを作成し、期限切れのパーティションをドロップします。通常はスケジュールされたジョブ(例: cron)として実行されます。
時系列テーブルの pg_partman のセットアップ方法の例を以下に示します。
1. pg_partman のインストール
まず、拡張機能をインストールする必要があります。通常、パッケージ(例: Debian/Ubuntu では postgresql-14-partman)をインストールし、データベース内で有効にすることを含みます。
CREATE EXTENSION pg_partman;
2. 親テーブルの作成
センサーの読み取り値を格納する sensor_data テーブルがあり、timestamp 列があると仮定しましょう。
CREATE TABLE public.sensor_data ( id BIGSERIAL NOT NULL, sensor_id INT NOT NULL, measurement_value NUMERIC(10, 2) NOT NULL, event_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ) PARTITION BY RANGE (event_timestamp); -- 定義されたパーティションの範囲外にあるデータに対して、初期のデフォルトパーティションを作成することは良い習慣です。 CREATE TABLE public.sensor_data_default PARTITION OF public.sensor_data DEFAULT;
PARTITION BY RANGE (event_timestamp) 句に注意してください。これは、event_timestamp 列に基づいて sensor_data がパーティション化されたテーブルであることを PostgreSQL に伝えます。
3. pg_partman の構成
次に、pg_partman に public.sensor_data を管理するように指示します。日次パーティションを設定し、5 日先を予測し、30 日間のデータを保持します。
SELECT partman.create_parent( p_parent_table => 'public.sensor_data', p_control_column => 'event_timestamp', p_interval => 'daily', p_premake => '5 days', p_retention => '30 days' );
これらのパラメータの内訳は次のとおりです。
p_parent_table: 親テーブルのスキーマと名前(public.sensor_data)。p_control_column: パーティショニングに使用される列(event_timestamp)。p_interval: 各パーティションの期間(daily)。他のオプションには'hourly','weekly','monthly','yearly'、または'3 hours','15 minutes'のような特定の時間間隔があります。p_premake:pg_partmanが将来の空のパーティションをどのくらい作成するか。'5 days'は、次の 5 日間のパーティションを作成することを意味します。これにより、新しいデータが遅延なく常に挿入されるパーティションを持つことが保証されます。p_retention: パーティションが保持される期間。'30 days'は、30 日よりも古いパーティションがドロップされることを意味します。
create_parent を実行すると、pg_partman はすぐに最初のセットの子パーティションを作成します。
\d+ public.sensor_data
sensor_data_2023_10_26、sensor_data_2023_10_27 など、似たようなものが表示されるはずです。
4. 保守関数の実行
pg_partman の自動化の核心は、その保守関数にあります。この関数は定期的に呼び出す必要があります。
SELECT partman.run_maintenance();
run_maintenance() が実行されると、partman.part_config テーブルに基づいていくつかのタスクを実行します。
- 新しいパーティションの作成:
p_premakeをチェックし、必要な将来のパーティションを作成します。 - 古いパーティションのドロップ:
p_retentionをチェックし、指定された保持期間よりも古いパーティションをドロップします。これはストレージとパフォーマンスの管理に不可欠です。 - パーティションのアタッチ: 非パーティション化されたテーブルに履歴データがある場合、
pg_partmanはそれらをアタッチするのに役立ちます。 - インデックスの管理: 親テーブルのインデックス定義に基づいて、新しいパーティションにインデックスを自動的に作成できます。
5. 保守のスケジュール
run_maintenance() の呼び出しは、定期的に実行されるようにスケジュールする必要があります。一般的なアプローチは、データベースサーバー上の cron や専用のスケジューラを使用することです。たとえば、毎時間実行するには:
# crontab で (例: `crontab -e`) 0 * * * * psql -d your_database -c "SELECT partman.run_maintenance();"
アプリケーションシナリオと高度な機能
- 履歴データ移行:
pg_partmanは、既存のデータを非パーティション化されたテーブルから新しく作成されたパーティション化された子テーブルに移動する機能を提供します。 - カスタム命名規則: デフォルトの
tablename_YYYY_MM_DD形式が適切でない場合、子パーティションの命名をカスタマイズできます。 - インデックス管理:
pg_partmanは new partition にインデックスを作成するように構成でき、親テーブルにミラーリングされます。これはクエリパフォーマンスに不可欠です。 - テンプレート: より複雑なパーティショニングスキームや、新しいパーティションに特定のテーブル/インデックスストレージパラメータを適用するために、
pg_partmanはテンプレートテーブルをサポートします。 - バックグラウンドワーカー: 非常にアクティブなシステムの場合、
pg_partmanは PostgreSQL 内のバックグラウンドワーカープロセスとしても実行でき、外部スケジューリングの問題を回避できます。 - パーティションアーカイブ: 古いパーティションを単にドロップする代わりに、
pg_partmanはそれらをデタッチするように構成でき、削除前にコールドストレージに移動したりアーカイブしたりできます。
これらのプロセスを自動化することで、pg_partman は大規模なパーティション化されたテーブルを管理する運用オーバーヘッドを大幅に削減し、開発者や DBA がインフラストラクチャ管理ではなくデータ活用に集中できるようにします。パーティションプルーニングを活用したクエリパフォーマンスを確保し、定期的なデータパージによるストレージの増加を制御します。
結論
適切な自動化なしに PostgreSQL でテラバイト規模の時系列データを管理することは、パフォーマンスのボトルネックと管理の複雑さに満ちた、困難な戦いです。pg_partman は、堅牢なライフサイクル管理で PostgreSQL のネイティブパーティショニング機能をシームレスに拡張する、不可欠なツールとして登場します。数千のパーティションの作成、保持、メンテナンスを簡素化し、最適なデータベースパフォーマンスと予測可能なストレージコストを保証します。大規模な時間ベースのデータセットを扱う PostgreSQL デプロイメントにとって、pg_partman は単なる利便性ではなく、スケーラブルで保守可能な運用には不可欠です。

