Postgres 9.1データベースには、table1
約150万行と1列のテーブルがありますlabel
(この質問のために簡略化された名前)。
機能的なtrigram-indexがありますlower(unaccent(label))
(インデックスでunaccent()
使用できるように不変にされています)。
次のクエリは非常に高速です。
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
ただし、次のクエリは遅くなります。
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
また、検索がより厳密であっても、単語の追加はさらに遅くなります。
私は最初の単語のサブクエリを実行し、次に完全な検索文字列でクエリを実行する簡単なトリックを試しましたが、クエリプランナは(悲しいことに)私の陰謀を見ました:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
テーブル1のビットマップヒープスキャン(コスト= 16216.01..16220.04行= 1幅= 212)(実際の時間= 1824.017..1824.019行= 1ループ= 1) Condの再チェック:((lower(unaccent((label):: text))~~ '%someword%' :: text)AND(lower(unaccent((label):: text))~~ '%somewordなど%'::テキスト)) -> table1_label_hun_gin_trgmのビットマップインデックススキャン(cost = 0.00..16216.01 rows = 1 width = 0)(actual time = 1823.900..1823.900 rows = 1 loops = 1) インデックス条件:((lower(unaccent((label):: text))~~ '%someword%' :: text)AND(lower(unaccent((label):: text))~~ '%somewordなど%'::テキスト)) 合計ランタイム:1824.064ミリ秒
私の究極の問題は、検索文字列が非常に長い文字列を送信するWebインターフェースに由来するため、非常に遅くなり、DOSベクトルを構成する可能性があることです。
だから私の質問は:
- クエリを高速化する方法は?
- より速くなるようにサブクエリに分割する方法はありますか?
- 後のバージョンのPostgresの方が良いのでしょうか?(私は9.4を試してみましたが、速くは見えません。それでも同じ効果です。後のバージョンでしょうか?)
- 別のインデックス作成戦略が必要な場合がありますか?
unaccent
不変を宣言しました。これを質問に追加しました。
unaccent
モジュールを更新すると、ハックが上書きされることに注意してください。代わりに関数ラッパーを提案する理由の1つ。
unaccent()
追加モジュールによって提供されることにも言及する必要があります。Postgresはデフォルトではないため、関数のインデックスをサポートしていませんIMMUTABLE
。何かを変更したに違いないので、質問で正確に何をしたかを述べてください。私の立ってアドバイス:stackoverflow.com/a/11007216/939860。また、トライグラムインデックスは、大文字と小文字を区別しない一致なしのマッチングをサポートします。次のように単純化できます。-WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
一致するインデックス。詳細:stackoverflow.com/a/28636000/939860。