「パーティショニング、どうやってる?」
現場で若手と話をすると、たまにこんな話題になります。PostgreSQLのパーティショニングといえば、今はもう「宣言的パーティショニング(Declarative Partitioning)」一択だよね、というのが現代の常識です。
でもね、たまに古いシステムを保守していると、「テーブル継承(Table Inheritance)」を使った、いわば”職人芸”のようなパーティショニングに出くわすことがあります。
今日は、この「旧来のパーティショニング」がなぜ生まれ、そしてなぜ今、私たちは新しい手法を選ぶべきなのか。その中身を少し深掘りしてみましょう。
—
継承ベースのパーティショニング、懐かしの裏側
PostgreSQLの「継承」機能は、元々オブジェクト指向的なテーブル設計のためにあったものですが、これを応用して巨大なテーブルを分割するのが、かつての定石でした。
仕組みはシンプルです。
1. 親テーブルを作る(これはデータを持たない「入れ物」)。
2. 子テーブルを親から継承して作る(ここが実際のデータの置き場所)。
3. 子テーブルに`CHECK制約`を付け、どの範囲のデータが入るかを明示する。
4. 親テーブルにトリガーを仕込み、INSERTされたデータを適切な子テーブルへ「ルーティング」する。
実装のイメージはこんな感じ
例えば、月次でデータを保存するログテーブルを作るなら、こんな風に書いていました。
— 親テーブル
CREATE TABLE logs (
id serial,
created_at timestamp NOT NULL,
message text
);
— 子テーブル(2023年10月分)
CREATE TABLE logs_2023_10 (
CHECK (created_at >= ‘2023-10-01’ AND created_at < '2023-11-01')
) INHERITS (logs);
-- ルーティング用のトリガー関数
CREATE OR REPLACE FUNCTION logs_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF (NEW.created_at >= ‘2023-10-01’ AND NEW.created_at < '2023-11-01') THEN
INSERT INTO logs_2023_10 VALUES (NEW.);
-- ここに延々と月ごとのIF文やCASE文を書いていく…
ELSE
RAISE EXCEPTION '日付が範囲外です';
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER insert_logs_trigger
BEFORE INSERT ON logs
FOR EACH ROW EXECUTE FUNCTION logs_insert_trigger();
---
なぜこの手法は「卒業」されたのか?
コードを見て気づいた人もいるでしょう。……そう、運用がとにかく面倒なんです。
- トリガーの保守地獄: 新しい月が来るたびに、トリガー関数を書き換えるか、あるいは動的に生成するスクリプトを回し続ける必要があります。このメンテを忘れると、データが親テーブルに溜まってパフォーマンスが急落します。
- プランナの負荷: 親テーブルに対してクエリを投げると、PostgreSQLはすべての継承テーブルをチェックして、`CHECK制約`を元に「どのテーブルを見るべきか」を判断します。テーブル数が増えると、この「制約排除(Constraint Exclusion)」のコストがバカになりません。
- DDLの複雑さ: インデックス一つ追加するにも、親に貼れば自動で子に継承されますが、排他的なロックが長時間かかったり、意図しない挙動に悩まされることが多々ありました。
—
宣言的パーティショニングという「正解」
PostgreSQL 10で登場した「宣言的パーティショニング」は、この苦労をすべて過去のものにしました。
`PARTITION BY RANGE` と書けば、あとはPostgreSQLが内部でよしなにやってくれます。トリガーなんて書く必要はありません。
CREATE TABLE logs (
id serial,
created_at timestamp NOT NULL,
message text
) PARTITION BY RANGE (created_at);
CREATE TABLE logs_2023_10 PARTITION OF logs
FOR VALUES FROM (‘2023-10-01’) TO (‘2023-11-01’);
これだけで、ルーティングはエンジンレベルで最適化されますし、パーティションの追加・切り離しも`DETACH PARTITION`一発で完了です。ロックの競合も最小限に抑えられます。
—
最後に:先輩からのアドバイス
もしあなたが今、新規で設計をしているなら、継承ベースのパーティショニングを使う理由は1ミリもありません。 「昔のコードに似ているから」という理由で継承を使い続けるのは、ただの技術的負債の先送りです。
一方で、もしレガシーなシステムを触っているなら、この「トリガーと継承」の仕組みを理解しておくことは、非常に価値があります。「なぜ昔はこうだったのか?」「今の仕組みは何を解決したのか?」を知っているエンジニアは、トラブルシューティングの際の引き出しが違います。
もし今の現場で継承ベースの手法に苦しんでいるなら、少しずつ「宣言的パーティショニング」への移行を検討してみてください。その工数は、将来の自分たちへの一番のプレゼントになりますから。
それでは、また現場でお会いしましょう!
コメント