マテリアライズドビューを使用して困ったこと

データベース環境:PostgreSQL
とある画面の表示性能が遅いという要望を受けました。
ボトルネックになっているのが画面表示データを抽出するSQLが起因でありました。
大量データを扱うシステムのため、複雑なクエリを組んでいた事が表示性能劣化の原因ですが、
その検索速度を高速に処理するためにマテリアライズドビュー(以下、マテビュー) を導入することにしました。
■マテリアライズドビューとは
マテビューは、クエリ結果を事前に計算して保存することで、
検索を高速化できる便利な仕組みです。
一度作成してしまえば、そのマテビューをテーブルのように扱うことができます。
しかし、データの鮮度を保つためには REFRESH によるマテビューの更新が必要です。
ここで選択肢は2つあります:
①REFRESH MATERIALIZED VIEW
→ ビューをロックして再構築(更新中は参照不可)
②REFRESH MATERIALIZED VIEW CONCURRENTLY
→ トランザクションを分けて更新し、参照を継続可能(ただし制約あり)
今回は、サービスの可用性を重視し、REFRESH方式は② の
CONCURRENTLY オプションを使用することにしました。
1時間に1回、REFRESHをするバッチ処理を組み込み、
データ鮮度を保つことにしました。
■困ったこと
導入直後は期待通りのパフォーマンスを発揮していましたが、
時間の経過とともに検索速度が低下する問題が発生しました。
■原因
REFRESH MATERIALIZED VIEW CONCURRENTLY は、
既存データを削除せずに差分更新を行います。
このとき、古い行は即座に物理的に消えず、
デッドタプル(不要になった行データ) として残ります。
デッドタプルの量は、マテビューに使用している基となる
テーブルの更新量(差分の大きさ)に比例して増加します。
今回使用したテーブルは、頻繁に更新が行われるテーブルだったため、
デッドタプルが大量に溜まり、テーブルサイズが肥大化した結果、
検索性能が低下していました。
■対応
1時間に1回のバッチ処理にてマテビューを更新する際、
REFRESH だけでなく、VACUUM と ANALYZE を定期的に実行するようにしました。
Vacuum : https://www.postgresql.jp/docs/9.4/sql-vacuum.html
Analyze : https://www.postgresql.jp/docs/9.4/sql-analyze.html
これにより、VACUUM処理にて、デッドタプルが占有していた領域を解放し、
さらにANALYZE処理にて統計情報を最新化することでクエリの実行計画が最適化され、
結果、検索速度の低下を防ぐことができました。
1時間に1回VacuumとAnalyzeをするという処理コストはどうしてもかかってしまいますが、
画面描画の性能劣化は完全に改善することができました。
■まとめ
マテビューは便利ですが、REFRESH CONCURRENTLY を使う場合は
デッドタプル対策と統計情報更新が必須です。
「REFRESH + VACUUM + ANALYZE」 の組み合わせを定期的に行うことで、
安定した処理速度を維持できます。

