「毎回計算させるの、もうやめない?」PostgreSQLのマテリアライズドビュー活用術
現場でバリバリ開発していると、一度は直面するよね。「このクエリ、重すぎて話にならない」っていう壁。
JOINを重ねまくった複雑なレポート用クエリや、数百万行のテーブルをガッツリ集計するSQL。これをダッシュボードを開くたびに走らせていたら、PostgreSQLも悲鳴を上げるし、何よりユーザーのUXが最悪だ。
そんな時、「普通のビュー(View)でいいじゃん」って思うかもしれない。でも、普通のビューは「アクセスされるたびに裏で元のクエリを再実行している」だけ。根本的な解決にはならないんだよね。
そこで今回紹介したいのが「マテリアライズドビュー(Materialized View)」だ。これ、知っておくと現場での立ち回りがガラッと変わるよ。
—
マテリアライズドビューとは?
簡単に言うと、「クエリの結果を物理的なテーブルとしてディスクに保存しておくビュー」のことだ。
普通のビューが「レシピ(クエリ)」を保存しているのに対し、マテリアライズドビューは「調理済み料理(結果データ)」を冷蔵庫に保存しているイメージ。だから、取り出すときは超高速。計算コストを「参照時」から「更新時」にシフトさせる戦略だね。
さっそく作ってみよう
例えば、ECサイトの月間売上レポートを出すクエリがあるとしよう。
CREATE MATERIALIZED VIEW monthly_sales_summary AS
SELECT
date_trunc(‘month’, order_date) AS sales_month,
product_id,
sum(amount) AS total_amount
FROM orders
GROUP BY 1, 2;
これを作るだけで、以降は `SELECT FROM monthly_sales_summary` とするだけで、集計処理をすっ飛ばして結果が返ってくる。これ、初めて使うと感動するよ。
—
注意点:データは「勝手に」新しくならない
ここが一番の落とし穴なんだけど、マテリアライズドビューは作成した時点の「スナップショット」に過ぎない。元の `orders` テーブルに新しい注文が入っても、マテリアライズドビューの中身は古いまま。
そこで登場するのが `REFRESH MATERIALIZED VIEW` コマンドだ。
— 最新データに更新する
REFRESH MATERIALIZED VIEW monthly_sales_summary;
実務的なアドバイス
この更新コマンド、実行中は読み取りロックがかかってしまうことがある。大量のデータだと、更新が終わるまでビューへのアクセスが止まってしまうんだよね。
そんな時はこう書こう。
— CONCURRENTLY を付けると、ロックをかけずにバックグラウンドで更新できる
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales_summary;
※ただし、これを使うにはビューに「一意インデックス(Unique Index)」が貼ってある必要があるから忘れないように。
—
どんな時に使うべき?
万能薬じゃないから、使いどころは見極めよう。
- 向いているケース:
- データの参照頻度が非常に高い。
- クエリの実行コストがめちゃくちゃ高い(数秒〜数十秒かかる)。
- リアルタイム性がそこまで厳密に求められない(1時間ごとの更新でOK、など)。
- 向いていないケース:
- 常に最新のデータが必要なトランザクション処理。
- データ更新の頻度が凄まじく高く、REFRESHが追いつかない。
最後に:先輩からのアドバイス
実務では、この `REFRESH` をどう回すかが鍵になる。僕なら `pg_cron` を使って深夜にバッチ実行するか、あるいはトリガーを使って更新を制御するかな。
「とりあえずビューを作ればいいや」と安易に考えるんじゃなくて、「これはリアルタイム性が必要か? それとも計算済み結果をキャッシュすべきか?」という視点を持つこと。これができるエンジニアは、DBのパフォーマンスチューニングで絶対に重宝されるよ。
まずは自分のプロジェクトの「重いクエリ」を一つ選んで、試しにマテリアライズドビュー化してみてよ。その爆速っぷりに、きっとニヤリとするはずだから。
また何か詰まったら、いつでも聞いてくれ!
コメント