PostgreSQL | SQLite+MySQL経験者向け、30日で習得するPostgreSQL:設計とパフォーマンス - Day22 パーティショニング

SQL PostgreSQL
スポンサーリンク

Day22 後半のゴール

「“どう分けるか・どう使うか”を具体的な設計レベルでイメージする」

前半で「なぜ分けるか」「日付やIDで分けると何が嬉しいか」は掴めました。
後半では、もう一歩踏み込んで「実際にどう設計するか」「どこで失敗しやすいか」「セキュリティや運用まで含めてどう考えるか」を、例題ベースで整理していきます。


例題1:月ごとのログパーティションを“運用目線”で考える

「作る・読む・捨てるをセットで設計する」

前半の access_logs を、月ごとに分けるケースをもう少し“運用寄り”に見てみます。

やりたいことはシンプルです。
ログは毎日増える。
最近30日分はよく読む。
半年より前のログは、たまにしか見ないか、もう見ない。

このとき、パーティショニングを前提にすると、設計の発想が変わります。

まず、「パーティションをいつ作るか」を決める必要があります。
例えば、「毎月1日に、その月用のパーティションを自動で作る」運用です。
これはアプリ側のバッチでもいいし、DB側のスケジュールでもいい。

次に、「クエリがどのパーティションを読むか」を意識します。
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' のような条件をきちんと書いておけば、
PostgreSQL が「関係する月だけ読む」ようにプランを立ててくれます。
逆に、WHERE created_at >= '2024-01-01' のように広すぎる条件ばかりだと、
結局全部のパーティションを読むことになり、旨味が減ります。

最後に、「いつ捨てるか」です。
例えば「1年前より古いパーティションは DROP する」と決めておけば、
巨大な DELETE を打たなくても、テーブルごと一瞬で消せます。
これはロックも小さく、ディスクも一気に空くので、運用上かなり楽になります。

重要なのは、「パーティショニングはテーブル定義だけの話ではなく、
“作る・読む・捨てる”というライフサイクル全体の設計だ」という感覚です。


例題2:テナントごとのパーティショニングとセキュリティ

「“顧客ごとに物理的に分ける”という選択肢」

次に、SaaS っぽい例を考えます。
複数の企業(テナント)が同じアプリを使っていて、
それぞれのデータは論理的には分離されているが、同じDBに入っている、という状況です。

テーブルに tenant_id カラムがあり、
全てのクエリが WHERE tenant_id = ? を付けている、という世界ですね。

ここで、「テナントごとにパーティションを分ける」という設計をすると、
セキュリティと運用の両方でメリットが出てきます。

セキュリティ面では、
「あるテナントのデータだけを別サーバに移したい」「あるテナントだけ暗号化ストレージに置きたい」といった要望に対して、
そのテナントのパーティションだけを移動する、という選択肢が取れます。
物理的な分離がしやすくなるわけです。

運用面では、
「このテナントだけデータ量が異常に多い」「このテナントだけ負荷が高い」といったときに、
そのパーティションだけを別インスタンスに切り出す、というスケール戦略も見えてきます。

ただし、テナント数が多すぎるとパーティション数も増えすぎてしまい、
管理が難しくなります。
だから、「どの単位で分けるか」は慎重に選ぶ必要があります。

ここでの重要ポイントは、「パーティショニングは“性能のため”だけでなく、“顧客ごとの分離・保護”にも使える」という視点です。


パーティショニングで失敗しやすいポイント

「分けたのに、クエリがそれを活かしていない」

よくある失敗は、「パーティションを頑張って作ったのに、クエリがそれを活かしていない」パターンです。

例えば、日付で分けたのに、
クエリ側が WHERE created_at >= '2000-01-01' のような広すぎる条件ばかり書いていると、
結局全パーティションを読むことになります。

あるいは、パーティションキーと関係ないカラムでばかり絞り込んでいると、
パーティションプルーニングが効かず、「分けた意味」が薄くなります。

ここで大事なのは、「パーティションキーをクエリ設計にも反映する」ことです。
ログなら「期間で絞る」クエリを中心に設計する。
テナントなら「tenant_id で絞る」クエリを前提にする。

パーティショニングは、「テーブル定義」と「クエリの書き方」がセットで初めて効いてきます。
どちらか片方だけだと、効果が半減します。


パーティショニングとインデックスの関係

「“分けた上で、各パーティションの中もちゃんと速くする”」

パーティショニングすると、「テーブルが分かれたから速くなる」と思いがちですが、
各パーティションの中身がちゃんとインデックス設計されていないと、
結局その中での検索が遅くなります。

例えば、access_logs を月ごとに分けたとしても、
user_idpath にインデックスがなければ、
「特定ユーザーの最近30日分のログ」を取るクエリは、
各パーティション内でフルスキャンになります。

つまり、

パーティショニングで「読むべきテーブルの数」を減らす。
インデックスで「各テーブル内の探索コスト」を減らす。

この二段構えが必要です。

PostgreSQL の宣言的パーティショニングでは、
親テーブルにインデックスを張ると、子パーティションにも同じインデックスが作られる仕組みがあります。
これをうまく使うと、「パーティション+インデックス」をセットで管理しやすくなります。

重要なのは、「パーティショニングをしたからインデックスはいらない」ということは絶対にない、という理解です。


パーティショニング移行時の“怖さ”と向き合う

「既存テーブルを分割するときに何が起きるか」

実務で一番緊張するのが、「すでに運用中の巨大テーブルをパーティショニングに移行する」ときです。

ここでは、ざっくり何が起きるかをイメージしておきましょう。

まず、既存テーブルのデータを、パーティション構造に合わせて再配置する必要があります。
これは、INSERT…SELECT や専用ツールを使って行うことが多く、
時間もかかるし、ロックや負荷も発生します。

次に、アプリ側のクエリが「新しい構造でも正しく動くか」を検証する必要があります。
論理的には同じテーブル名で見えるようにしても、
実行計画やロックの挙動が変わることがあります。

さらに、バックアップ・監査・運用スクリプトなど、
周辺の仕組みも「パーティション前提」に書き換える必要が出てきます。

このように、「後からパーティショニングに移行する」のは、
それなりに大きなプロジェクトになります。

だからこそ、「このテーブルは将来パーティショニングが必要になるかもしれない」と感じたら、
早めに検討を始めることが大事です。
ギリギリまで放置すると、移行のコストとリスクが跳ね上がります。


セキュリティ・監査から見たパーティショニング

「“どのデータをどこに置くか”を設計で決めておく」

セキュリティ・監査の視点でパーティショニングを見ると、
「データの置き場所を設計でコントロールする」ための武器になります。

例えば、

EU圏ユーザーのデータは、EU内のストレージに置きたい。
特定顧客のデータは、専用のインフラに分離したい。

といった要件が出てきたとき、
country_code や tenant_id でパーティショニングしておけば、
「そのパーティションだけを別クラスタに移す」という選択肢が現実的になります。

監査の観点でも、

「このパーティションには、どの期間・どのテナントのデータが入っているか」
「このパーティションを削除すると、どの範囲のログが消えるか」

を説明しやすくなります。

パーティショニングは、「性能チューニングのテクニック」だけでなく、
「データの境界線を引くための設計手段」でもある、ということを覚えておいてほしいです。


Day22 後半のまとめ

パーティショニングは、単に「テーブルを分けて速くする」だけでなく、「いつパーティションを作るか」「どのクエリがどのパーティションを読むか」「いつ・どう捨てるか」というライフサイクル全体の設計であり、ログなら期間、SaaSならテナントIDなど、パーティションキーをクエリ設計・運用・セキュリティ設計にまで反映させることで初めて本当の効果が出る。
そのうえで、「分けたのにクエリがそれを活かしていない」「インデックスを張らずに各パーティション内でフルスキャンしている」「既存テーブルからの移行をギリギリまで先送りする」といった典型的な落とし穴を避けつつ、「どのデータをどこに置くか」を意図を持って決められるようになることが、Day22 後半の着地点になる。

タイトルとURLをコピーしました