Dalam database Postgres 9.1, saya memiliki tabel table1
dengan ~ 1,5 juta baris dan kolom label
(nama yang disederhanakan untuk pertanyaan ini).
Ada trigram-indeks fungsional pada lower(unaccent(label))
( unaccent()
telah dibuat tidak dapat diubah untuk memungkinkan penggunaannya dalam indeks).
Permintaan berikut ini cukup cepat:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Tetapi kueri berikut lebih lambat:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
Dan menambahkan lebih banyak kata bahkan lebih lambat, meskipun pencariannya lebih ketat.
Saya mencoba trik sederhana untuk menjalankan subquery untuk kata pertama dan kemudian kueri dengan string pencarian lengkap, tetapi (sayangnya) perencana kueri melihat melalui intrik saya:
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%'));
Bitmap Heap Scan pada table1 (biaya = 16216.01..16220.04 baris = 1 lebar = 212) (waktu aktual = 1824.017..1824.019 baris = 1 loop = 1) Periksa kembali Cond: ((lebih rendah (unaccent ((label) :: text)) ~~ '% anyord%' :: text) AND (lebih rendah (unaccent ((label) :: text)) ~~ '% sortord dan beberapa lagi %'::teks)) -> Pemindaian Indeks Bitmap pada table1_label_hun_gin_trgm (biaya = 0,00..16216,01 baris = 1 lebar = 0) (waktu aktual = 1823.900..1823.900 baris = 1 putaran = 1) Indeks Cond: ((lebih rendah (unaccent ((label) :: text)) ~~ '% anyord%' :: text) AND (lebih rendah (unaccent ((label) :: text)) ~~ '% anyord dan beberapa lagi %'::teks)) Total runtime: 1824.064 ms
Masalah utama saya adalah bahwa string pencarian berasal dari antarmuka web yang dapat mengirim string yang cukup panjang dan karenanya sangat lambat dan juga merupakan vektor DOS.
Jadi pertanyaan saya adalah:
- Bagaimana cara mempercepat kueri?
- Apakah ada cara untuk memecahnya menjadi subqueries sehingga lebih cepat?
- Mungkin versi Postgres yang lebih baru lebih baik? (Saya mencoba 9,4 dan sepertinya tidak lebih cepat: masih efek yang sama. Mungkin versi yang lebih baru?)
- Mungkin diperlukan strategi pengindeksan yang berbeda?
unaccent
abadi. Saya menambahkan ini ke pertanyaan.
unaccent
modul. Salah satu alasan mengapa saya menyarankan pembungkus fungsi sebagai gantinya.
unaccent()
ini juga disediakan oleh modul tambahan dan Postgres tidak mendukung indeks pada fungsi secara default karena tidakIMMUTABLE
. Anda harus mengubah sesuatu dan Anda harus menyebutkan apa yang Anda lakukan persis dalam pertanyaan Anda. Saran berdiri saya: stackoverflow.com/a/11007216/939860 . Juga, indeks trigram mendukung pencocokan case-sensitive di luar kotak. Anda dapat menyederhanakan untuk:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- dengan indeks yang cocok. Detail: stackoverflow.com/a/28636000/939860 .