全文検索を高速化するために、2種類のインデックスが使えます。全文検索のためにインデックスが必須だと言うわけではないことを言っておかなければなりませんが、日常的に検索される列には、インデックスを使った方が良いでしょう。
CREATE INDEX name ON table USING gist(column);
GiST (Generalized Search Tree)インデックスを作ります。columnは tsvector またはtsquery 型です。
CREATE INDEX name ON table USING gin(column);
GIN (Generalized Inverted Index)インデックスを作ります。 columnはtsvector型でなけれ ばなりません。
GiSTインデックスは、非可逆です。つまり、インデックスは間違った結果を返すかも知れないので、間違った結果を排除するために、テーブルの行をチェックすることが必要です。PostgreSQLはこれを自動的に行います。たとえば、以下の問合わせプランでは、Filter:行がインデックスの生成結果を再度チェックしていることを示しています。
EXPLAIN SELECT * FROM apod WHERE textsearch @@ to_tsquery('supernovae'); QUERY PLAN ------------------------------------------------------------------------- Index Scan using textsearch_gidx on apod (cost=0.00..12.29 rows=2 width=1469) Index Cond: (textsearch @@ '''supernova'''::tsquery) Filter: (textsearch @@ '''supernova'''::tsquery)
GiSTインデックスが非可逆なのは、インデックス中の各文書が固定長の署名で表現されているからです。署名は、各々の単語をハッシュしてランダムなビットにして、これらのビットをnビットの文書署名にORし、nビットの列中のビットにすることで実現されています。2つの単語が同じビット位置を生成すると、間違った一致が起こります。問い合わせ対象のすべての単語が照合すると(それが正しいか間違っているかは別として)、その照合が正しいものかどうかテーブルの行を取得して調べなければなりません。
非可逆性は、間違った照合によるテーブルからの不必要なデータ取得のため、性能を劣化させます。テーブルへのランダムアクセスは遅いので、GiSTインデックスの有用性は制限されています。誤った照合がどの位あるかという可能性はいくつか要因によりますが、とりわけユニークな単語の数に依存します。ですから、辞書を使ってユニークな単語の数を減らすことをお勧めします。
GINインデックスは非可逆ではありませんが、その性能はユニークな単語の数の対数に依存します。
実際にはGINインデックスはtsvector値の中の単語(語彙素)のみを保持しており、重み付けラベルは持っていません。ですから、GINインデックスは重み付けを指定しない問合わせに関しては可逆と考えられますが、重み付けがあるものに関しては非可逆です。したがって、重み付けを伴う問合わせではテーブルの行を再チェックしなければなりません。残念ながら、PostgreSQLの現在の設計では、再チェックが必要かどうかというのは特定の演算子の静的な性質であり、演算子に与えられた値に依存して動的に有効にしたり無効にしたりできません。再チェックを必要としない問合わせにおいて、そのオーバヘッドを避けるために、次の方法が採用されています。
テキスト一致演算子@@はGINインデックスでは可逆とマークされています。
更に追加の一致演算子@@@が提供されており、GINインデックスでは非可逆とマークされています。この演算子はそれ以外は@@とまったく同じ動作をします。
GINインデックス検索が@@演算子で起動された際に、問合わせが重みを指定していると、インデックス支援コードがエラーを投げます。これによって、重み付けの再チェックを行わないことによる誤動作を防ぐことができます。
まとめると、重み付けを伴う問合わせをGINインデックスで実行する際は、@@ではなく、@@@を使わなければならないということです。重み付けを伴わない問合わせでは、どちらを使っても構いませんが、@@の方が高速でしょう。この不便さは、PostgreSQLの将来のリリースで対策が取られると思います。
GiST、GINのどちらのインデックス形式を選ぶにあたっては、以下の性能上の違いを考慮してください。
GINインデックスの検索はGiSTの約3倍高速です
GINインデックスの構築はGiSTの約3倍時間がかかります
GINインデックスに対する更新はGiSTの約10倍低速です
GINインデックスは、2から3倍GiSTよりも大きいです
大雑把に言うと、GINインデックスは検索が高速なので、静的なデータにもっとも向いています。動的なデータには、GiSTインデックスの更新が高速です。とりわけ、GiSTインデックスは、動的なデータに非常に向いており、ユニークな単語(語彙素)が100,000未満ならば高速です。一方GINインデックスは100,000以上の語彙素をよりうまく扱うことができますが、更新が遅いです。
GINインデックスの構築時間はmaintenance_work_memを増やすことによってしばしば改善することができることに注意してください。一方GiSTインデックスの構築時間にはあまりそのパラメータは効きません。
大きなデータをパーティショニングし、GiST、GINインデックスを使うことによってオンラインの更新を伴いながら、非常に高速な検索を実現することができます。パーティショニングは、継承とconstraint_exclusionを使ってデータベースレベルで実現できます。あるいは、文書を複数のサーバに分散させ、contrib/dblink拡張モジュールを使って検索結果を集約できます。これは、ランキング関数がローカルな情報しか使わないために可能になります。