RUM 拡張機能は、大量のテキストに対する全文検索において、PostgreSQL に組み込まれている汎用逆引きインデックス (GIN) の代替手段を提供します。RUM 拡張機能を使用すると、検索結果を関連度、タイムスタンプ、またはその他のフィールドでソートできます。従来の GIN インデックスでは、結果をソートするためにテーブルルックアップが必要であり、これがパフォーマンスのボトルネックになる可能性がありました。RUM 拡張機能は、位置情報やその他の必要な情報をインデックスに直接保存することで、これらのルックアップを回避します。これにより、特定のシナリオでクエリパフォーマンスを数倍向上させることができます。
利用シーン
以下の表で、RUM 拡張機能とネイティブの GIN インデックスの主な違いをご確認ください。この情報を参考に、お客様のビジネスシナリオに最適なオプションをご選択ください。
比較項目 | GIN インデックス (組み込み) | RUM インデックス | 推奨事項 |
主な利点 | 良好な書き込みパフォーマンス。比較的小さなインデックスサイズ。 | 高いソートパフォーマンス。フレーズ検索と添付列によるソートをサポート。 | 検索結果のソートが必要なシナリオでは、RUM 拡張機能が推奨されます。 |
ソートパフォーマンス | 遅い。ソートデータを取得するためにテーブルルックアップが必要。データ量が増加するとパフォーマンスが急激に低下します。 | 速い。インデックス内で直接ソートします。テーブルルックアップは不要です。 | 関連度、時間、価格などで全文検索結果を頻繁にソートする場合は、RUM 拡張機能を使用してください。 |
フレーズ検索 | 遅い。フレーズを検証するために単語の位置情報を取得するためのテーブルルックアップが必要です。 | 速い。単語の位置情報はすでにインデックスに保存されています。テーブルルックアップは不要です。 | 高性能なフレーズ検索が必要なシナリオでは、RUM 拡張機能を使用してください。 |
添付列によるソート | 非対応。タイムスタンプなどの追加列の情報をインデックスに保存できません。 | 対応。他の列をインデックスにアタッチして、高性能なカスタムソートを実現できます。 | 記事の公開時刻や更新時刻などのフィールドでソートする必要があるシナリオでは、RUM 拡張機能に大きな利点があります。 |
書き込みパフォーマンス | 比較的速い。 | より複雑なインデックススキーマを維持する必要があるため、比較的遅い。 |
|
インデックスサイズ | 比較的小さい。 | 位置情報や追加情報を保存するために余分な領域が必要なため、比較的大きい。 | ストレージコストを評価してください。インデックスサイズが主なボトルネックである場合は、RUM |
プレフィックス検索 | 対応。 |
| プレフィックス検索がコア要件である場合は、RUM |
RUM 拡張機能は、領域を犠牲にして時間を短縮するトレードオフの関係にあります。インデックスサイズと書き込みオーバーヘッドを増加させる代わりに、特定のクエリシナリオ、特にソートのパフォーマンスを大幅に向上させます。コアビジネスが複雑なソートを伴う全文検索である場合、RUM 拡張機能は理想的な選択肢です。ビジネスが主に書き込み集中型であるか、単純なテキストマッチングのみを必要とする場合は、GIN インデックスの方がコスト効率が高いです。
注意事項
書き込みパフォーマンスとインデックスサイズ:クエリを高速化するために、RUM インデックスは単語の位置などの追加情報を保存します。これにより、インデックスサイズが GIN インデックスよりも大きくなり、データ書き込みおよび更新操作中のインデックス作成オーバーヘッドが増加します。したがって、ストレージ領域が懸念される書き込み集中型のシナリオでは、RUM 拡張機能のコストを慎重に評価する必要があります。
プレフィックス検索をサポートしないシナリオ:
rum_tsvector_hash_opsまたはrum_tsvector_hash_addon_opsオペレータークラスで作成されたインデックスは、プレフィックス検索をサポートしません。これは、元のテキストではなく、字句のハッシュ値を保存するためです。
拡張機能のインストールとアンインストール
拡張機能のインストール
データベースで次のコマンドを実行して、RUM 拡張機能を作成します。
CREATE EXTENSION rum;拡張機能のアンインストール
RUM 拡張機能が不要になった場合は、次のコマンドを実行してアンインストールします。
DROP EXTENSION rum;使用方法
RUM 拡張機能は、さまざまなデータ型とクエリシナリオをサポートするために、複数のオペレータークラスを提供します。特定のニーズに基づいて、適切なオペレータークラスを選択してインデックスを作成できます。
オペレータークラスは、RUM インデックスが特定のデータ型を処理するために使用する一連の操作を定義します。これにより、インデックスはその型のデータを正しく保存および取得できます。各オペレータークラスには、特定のオペレーターセットが含まれています。これらのサポートされているオペレーターを WHERE または ORDER BY 句で使用すると、PostgreSQL は RUM インデックスを使用してクエリを高速化できます。
したがって、正しいオペレータークラスを選択することが、RUM インデックスの有効性を確保するための鍵となります。詳細については、「オペレータークラスとオペレーターファミリー」をご参照ください。
オペレーター
オペレーター | サポートされる型 | 戻り値の型 | 説明 |
A | 左: |
| 全文検索ベクターがクエリ条件に一致するかどうかを返します。距離計算を実行します。 |
A | 左: |
| 全文検索ベクターとクエリ条件の間の距離値を返します。値が小さいほど関連度が高くなります。 |
|
| 2 つの値の絶対差を返します。
| |
A |
|
|
|
A |
|
|
|
オペレータークラス
オペレータークラス | 適用可能なデータ型 | 主要な対応演算子 | 主な機能と説明 |
|
|
|
|
|
|
|
|
|
|
| 非テキスト、非配列データ型に対して範囲クエリと距離ソートを実行します。 |
|
|
| 追加の列 (たとえば 説明 アタッチされた列のデータ型は、対応する |
|
|
|
|
|
|
|
|
|
|
| 配列型をインデックス化します。包含や重複などの配列操作をサポートし、配列間の距離によるソートをサポートします。 |
|
|
| 追加の列のデータを配列インデックスにアタッチして、より複雑なクエリシナリオをサポートします。 説明 アタッチされた列のデータ型は、対応する |
シナリオ 1:全文検索結果の関連度によるソートの高速化
全文検索の結果を関連度でソートする必要がある場合、RUM インデックスを使用して、GIN インデックスで必要となる追加のソートオーバーヘッドを回避し、高性能なソートを実現できます。
データの準備:まず、テストテーブルを作成します。
CREATE TABLE t1( t text, t_vec tsvector GENERATED ALWAYS AS (to_tsvector('pg_catalog.english', t)) STORED ); -- テストデータの挿入 INSERT INTO t1(t) VALUES ('The situation is most beautiful'); INSERT INTO t1(t) VALUES ('It is a beautiful'); INSERT INTO t1(t) VALUES ('It looks like a beautiful place');RUM インデックスの作成:
rum_tsvector_opsオペレータークラスを使用して、tsvector列に RUM インデックスを作成します。CREATE INDEX t1_t_vec_idx ON t1 USING rum (t_vec rum_tsvector_ops);関連度によるソートクエリの実行:
<=>オペレーターを使用してクエリとソートを実行します。このオペレーターは、クエリとテキストの間の距離を計算します。距離が小さいほど関連度が高いことを示します。したがって、ORDER BYを使用すると、結果が関連度順にソートされます。SET enable_seqscan TO off; SELECT t, t_vec <=> to_tsquery('english', 'beautiful | place') AS rank FROM t1 WHERE t_vec @@ to_tsquery('english', 'beautiful | place') ORDER BY t_vec <=> to_tsquery('english', 'beautiful | place');次の結果が返されます。
t | rank ---------------------------------+--------- It looks like a beautiful place | 8.22467 The situation is most beautiful | 16.4493 It is a beautiful | 16.4493
シナリオ 2:全文検索と添付列の複合ソートの高速化
ログ分析や E コマース検索などのシナリオでは、全文検索を実行し、タイムスタンプや価格などの追加フィールドで結果をソートする必要が頻繁にあります。RUM の アドオン機能を使用すると、添付列の情報をインデックスに保存できます。これにより、効率的な複合クエリとソートが可能になります。
データの準備:
tsvector列とタイムスタンプ列を含むテーブルを作成し、サンプルデータを挿入します。CREATE TABLE tsts (id int, t tsvector, d timestamp); INSERT INTO tsts VALUES (354, to_tsvector('wr qh'), '2016-05-16 14:21:22.326724'), (355, to_tsvector('wr qh'), '2016-05-16 13:21:22.326724'), (356, to_tsvector('ts op'), '2016-05-16 18:21:22.326724'), (358, to_tsvector('ts op'), '2016-05-16 23:21:22.326724'), (371, to_tsvector('wr qh'), '2016-05-17 06:21:22.326724'), (406, to_tsvector('wr qh'), '2016-05-18 17:21:22.326724'), (415, to_tsvector('wr qh'), '2016-05-19 02:21:22.326724');添付列を持つ RUM インデックスの作成:
rum_tsvector_addon_opsオペレータークラスを使用し、WITH句で添付列とプライマリインデックス列を指定します。CREATE INDEX tsts_idx ON tsts USING rum (t rum_tsvector_addon_ops, d) WITH (attach = 'd', to = 't');説明キーとなる構文
WITH (attach = 'd', to = 't')は、d列 (添付列、タイムスタンプ) の値をt列 (プライマリインデックス列、tsvector型) のインデックスエントリにアタッチします。これにより、データベースは単一のインデックススキャンで、t列のインデックスを全文検索に、アタッチされたd列の情報を効率的なソートに使用できます。このプロセスにより、テーブルルックアップが回避され、パフォーマンスが大幅に向上します。複合ソートクエリの実行:特定の単語を含むレコードをクエリし、そのタイムスタンプがターゲット時刻にどれだけ近いかでソートします。
SET enable_seqscan TO off; EXPLAIN (costs off) SELECT id, d, d <=> '2016-05-16 14:21:25' AS distance FROM tsts WHERE t @@ 'wr&qh' ORDER BY d <=> '2016-05-16 14:21:25' LIMIT 5;次の実行計画は、ソートとフィルタリングの両方が単一のインデックススキャンで完了することを示しています。
QUERY PLAN ------------------------------------------------------------------------------ Limit -> Index Scan using tsts_idx on tsts Index Cond: (t @@ '''wr'' & ''qh'''::tsquery) Order By: (d <=> '2016-05-16 14:21:25'::timestamp without time zone)次の結果が返されます。
id | d | distance -----+----------------------------+--------------- 354 | 2016-05-16 14:21:22.326724 | 2.673276 355 | 2016-05-16 13:21:22.326724 | 3602.673276 371 | 2016-05-17 06:21:22.326724 | 57597.326724 406 | 2016-05-18 17:21:22.326724 | 183597.326724 415 | 2016-05-19 02:21:22.326724 | 215997.326724
シナリオ 3:配列クエリと類似度によるソートの高速化
タグ付けシステムやユーザーペルソナなどのシナリオでは、特定の要素を含む配列を効率的にクエリし、重複度や類似度でソートする必要があります。
データの準備:
CREATE TABLE test_array (id serial, i int2[]); INSERT INTO test_array(i) VALUES ('{}'), ('{0}'), ('{1,2,3,4}'), ('{1,2,3}'), ('{1,2}'), ('{1}');配列用の RUM インデックスの作成:
rum_anyarray_opsオペレータークラスを使用して、配列列のインデックスを作成します。CREATE INDEX idx_array ON test_array USING rum (i rum_anyarray_ops);配列クエリとソートの実行:要素
1を含むレコードをクエリし、{1}との類似度でソートします。SELECT * FROM test_array WHERE i && '{1}' -- '&&' オペレーターは配列の重複を示します。 ORDER BY i <=> '{1}' ASC; -- '<=>' オペレーターは配列間の距離を計算します。値が小さいほど類似度が高くなります。次の結果が返されます。
i ----------- {1} {1,2} {1,2,3} {1,2,3,4}
シナリオ 4:転置インデックスによるクエリルールの高速マッチング
ユーザーサブスクリプションやアラートルールマッチングなどのシステムを構築する場合、記事などの新しいデータを、ユーザーがサブスクライブしたキーワードなどの多くの既存のクエリルールと迅速に照合する必要があります。RUM 拡張機能は、tsquery 型にインデックスを作成して、効率的な逆引きマッチングを実行することをサポートします。
クエリルールデータの準備:
CREATE TABLE query (id serial, q tsquery, tag text); INSERT INTO query (q, tag) VALUES ('supernova & star', 'sn'), ('black', 'color'), ('big & bang & black & hole', 'bang'), ('spiral & galaxy', 'shape'), ('black & hole', 'color');tsquery用の RUM インデックスの作成:CREATE INDEX query_idx ON query USING rum(q rum_tsquery_ops);逆引きマッチングクエリの実行:新しい記事の
tsvectorを使用して、すべての適格なtsqueryルールを照合します。SELECT * FROM query WHERE to_tsvector('black holes never exists before we think about them') @@ q;次の結果が返されます。
id | q | tag -----+----------+------- 2 | 'black' | color