Postgres 9.1 veritabanında, table1
~ 1.5M satır ve bir sütun label
(bu soru uğruna basitleştirilmiş adlar) içeren bir tablo var .
Üzerinde fonksiyonel bir trigram indeksi vardır lower(unaccent(label))
( indekste unaccent()
kullanılmasına izin vermek için değişmez hale getirilmiştir).
Aşağıdaki sorgu oldukça hızlıdır:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Ancak aşağıdaki sorgu daha yavaştır:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
Ve arama daha katı olmasına rağmen daha fazla kelime eklemek daha da yavaştır.
İlk kelime için bir alt sorgu ve sonra tam arama dizesi ile bir sorgu çalıştırmak için basit bir hile denedim, ama (ne yazık ki) sorgu planlayıcısı benim makineleri ile gördüm:
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%'));
Tablo1'de Bitmap Yığın Taraması (maliyet = 16216.01..16220.04 satır = 1 genişlik = 212) (gerçek zaman = 1824.017..1824.019 satır = 1 döngü = 1) Cond: Recheck Cond: ((alt (unaccent ((etiket) :: metin)) ~ ~ '% someword%' :: metin) AND (alt (unaccent ((label) :: metin)) ~~ '% someword ve daha fazlası %'::Metin)) -> Bitmap Dizini table1_label_hun_gin_trgm'de tarama (maliyet = 0.00..16216.01 satır = 1 genişlik = 0) (gerçek zaman = 1823.900..1823.900 satır = 1 döngü = 1) Endeks Koşulu: ((alt (unaccent ((etiket) :: metin)) ~~ '% someword%' :: text) AND (alt (unaccent ((label) :: metin)) ~~ '% someword ve daha fazlası %'::Metin)) Toplam çalışma süresi: 1824.064 ms
Benim en büyük sorunum arama dizesi oldukça uzun dizeleri gönderebilir ve böylece oldukça yavaş olabilir ve aynı zamanda bir DOS vektörü oluşturabilir bir web arayüzü geliyor olmasıdır.
Yani sorularım:
- Sorgu nasıl hızlandırılır?
- Daha hızlı olması için alt sorgulara bölmenin bir yolu var mı?
- Belki Postgres'in sonraki bir sürümü daha iyidir? (9.4'ü denedim ve daha hızlı görünmüyor: hala aynı etki. Belki daha sonraki bir sürüm?)
- Belki farklı bir indeksleme stratejisine ihtiyaç vardır?
unaccent
değişmez ilan ettim . Bunu soruya ekledim.
unaccent
Modülü güncellediğinizde bilgisayar korsanlığının üzerine yazıldığını unutmayın . Bunun yerine bir işlev sarmalayıcı önermemin nedenlerinden biri.
unaccent()
ayrıca ek modül tarafından sağlanır ve Postgres yok değil o değil çünkü varsayılan olarak işlevini dizinleri desteklemekIMMUTABLE
. Bir şeyi değiştirmiş olmalısınız ve sorunuzda tam olarak ne yaptığınızdan bahsetmelisiniz. Daimi tavsiyem: stackoverflow.com/a/11007216/939860 . Ayrıca, trigram dizinleri kutudan büyük / küçük harfe duyarsız eşleşmeyi destekler. Aşağıdakileri basitleştirebilirsiniz:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- eşleşen bir dizinle. Ayrıntılar: stackoverflow.com/a/28636000/939860 .