「インデックスを貼ったのに、なぜかクエリが遅い……」
PostgreSQLを触っていると、一度はこんな壁にぶつかりますよね。特に、位置情報(PostGIS)や範囲検索、あるいは全文検索で`GiST`インデックスを使い始めたとき。B-Treeと同じ感覚で使っていると、思わぬ落とし穴にハマることがあります。
今日は、そんな「GiSTインデックスの深淵」について、現場の実感値を交えてお話しします。
—
GiSTとは何者か?:B-Treeの「守備範囲」を超えて
まず前提として、B-Treeは「順序」が定義できるデータ(数値や文字列など)には最強です。でも、現実世界のデータはそう単純じゃない。「地図上の2点間の距離」や「期間の重なり」をB-Treeで完璧に表現するのは無理があります。
そこで登場するのが GiST (Generalized Search Tree) です。
GiSTの面白いところは、データそのものを厳密に保持するんじゃなくて、「そのデータが属する領域(Bounding Box)」を木構造で管理するという点です。ざっくり言うと、「ここらへんにデータがあるはず!」というアタリをつけて検索するイメージですね。
—
「Lossy(損失あり)」という避けて通れない現実
さて、ここからが本題です。GiSTを使っていてパフォーマンスが伸び悩む最大の理由、それは 「Lossy(損失)」 です。
GiSTは、インデックス内に保持する情報を圧縮したり、簡易的な表現(矩形など)に変換したりします。そのため、インデックスをたどった結果、「たぶんここにあるはずだけど、違うかもしれないから、念のため実データもチェックしとくね!」 という処理が発生します。
これを専門用語で「Lossy compression」と言います。
なぜLossyが起きるのか?
例えば、複雑なポリゴンデータを扱うとき、PostgreSQLはそれを「外接矩形(Bounding Box)」としてインデックスに保存します。検索クエリが飛んできたとき、まずはその矩形に重なるものをピックアップしますが、実際には「矩形は重なっているけど、中身のポリゴンは重なっていない」というケースが多々あるんです。
この「偽陽性(False Positive)」を排除するために、インデックスの絞り込みの後で、わざわざヒープ(実テーブル)までデータを見に行く必要がある。 これが、クエリを遅くする真犯人です。
—
実践:GiSTを最適化するための「現場の知恵」
では、このLossyとの付き合い方をどう工夫するか。いくつか鉄則があります。
1. データの「詰め込みすぎ」を防ぐ
GiSTインデックスを定義するとき、あまりに複雑すぎるオブジェクトを突っ込むと、外接矩形が巨大化してしまい、検索効率がガタ落ちします。
もし位置情報なら、必要以上に細かいポリゴンを保持せず、簡略化したデータと詳細データを分けるなどの設計が有効です。
2. FILLFACTORを調整する
B-Treeと違って、GiSTはツリー構造を動的に作り変えます。デフォルトのままだと、頻繁な更新でツリーがスカスカになったり、逆に詰め込みすぎて検索効率が悪化したりします。
— インデックス作成時にFILLFACTORを調整する
CREATE INDEX idx_gist_geom ON my_table USING GIST (geom) WITH (FILLFACTOR = 80);
更新頻度が高いテーブルなら、この`FILLFACTOR`を少し下げて、ツリーのノードに「余白」を作っておくのが定石です。これだけで、インデックスの再構築(ページ分割)コストが劇的に下がります。
3. スキーマ設計で「範囲」を絞る
特に日付範囲や数値範囲の検索では、`range`型を活用しましょう。例えば「期間」を扱うとき、開始と終了の2カラムでインデックスを貼るより、`tsrange`型で1つのカラムにしてGiSTを貼る方が、検索時のオーバーヘッドが圧倒的に少なくなります。
—
最後に:迷ったら「EXPLAIN ANALYZE」を信じろ
インデックスチューニングの鉄則ですが、最後は必ず `EXPLAIN ANALYZE` を叩いてください。
EXPLAIN ANALYZE
SELECT FROM my_table WHERE geom && ST_MakeEnvelope(0, 0, 10, 10);
ここで `Index Cond` と `Filter` がどう出ているかを見ます。もし `Filter` で大量の行が捨てられているなら、それは「インデックスがうまく絞り込めていない(Lossyが多発している)」証拠です。
GiSTは非常に強力ですが、魔法ではありません。「ざっくり絞り込んで、あとは実データで答え合わせをする」 というその特性を理解してあげれば、もっと快適に付き合えるはずです。
「GiSTが遅いな」と思ったら、まずはインデックスが保持している「領域」が広すぎないか、一度見直してみてくださいね。現場からは以上です!
コメント