PostgreSQL SEPERTI variasi kinerja kueri


112

Saya telah melihat variasi yang cukup besar dalam waktu respons terkait LIKEkueri ke tabel tertentu di database saya. Kadang-kadang saya akan mendapatkan hasil dalam 200-400 ms (sangat dapat diterima) tetapi di lain waktu mungkin membutuhkan waktu hingga 30 detik untuk memberikan hasil.

Saya memahami bahwa LIKEkueri sangat intensif sumber daya tetapi saya tidak mengerti mengapa akan ada perbedaan besar dalam waktu respons. Saya telah membangun indeks btree di owner1lapangan tetapi menurut saya itu tidak membantu dengan LIKEkueri. Ada yang punya ide?

Contoh SQL:

SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10

Saya juga mencoba:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10

Dan:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10

Dengan hasil yang serupa.
Jumlah Baris Tabel: sekitar 95.000.

Jawaban:


281

FTS tidak mendukung LIKE

The jawaban yang diterima sebelumnya tidak benar. Pencarian Teks Lengkap dengan indeks teks lengkapnya sama sekali bukan untuk LIKEoperator, ia memiliki operator sendiri dan tidak berfungsi untuk string arbitrer. Ini beroperasi pada kata-kata berdasarkan kamus dan stemming. Ini tidak mendukung awalan yang cocok untuk kata-kata , tetapi tidak dengan LIKEOperator:

Indeks trigram untuk LIKE

Instal modul tambahan pg_trgmyang menyediakan kelas operator untuk indeks trigram GIN dan GiST untuk mendukung semua LIKEdan ILIKEpola , bukan hanya yang berlabuh kiri:

Contoh indeks:

CREATE INDEX tbl_col_gin_trgm_idx  ON tbl USING gin  (col gin_trgm_ops);

Atau:

CREATE INDEX tbl_col_gist_trgm_idx ON tbl USING gist (col gist_trgm_ops);

Contoh kueri:

SELECT * FROM tbl WHERE col LIKE '%foo%';   -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%';  -- works case insensitively as well

Trigram? Bagaimana dengan string yang lebih pendek?

Kata-kata dengan kurang dari 3 huruf dalam nilai yang diindeks masih berfungsi. Manualnya:

Setiap kata dianggap memiliki dua spasi diawali dan satu spasi saat menentukan himpunan trigram yang terdapat dalam string.

Dan pola pencarian dengan kurang dari 3 huruf? Manualnya:

Baik untuk LIKEpencarian ekspresi reguler maupun regular, perlu diingat bahwa pola tanpa trigram yang dapat diekstrak akan merosot menjadi scan indeks penuh.

Artinya, pemindaian indeks indeks / bitmap masih berfungsi (rencana kueri untuk pernyataan yang disiapkan tidak akan rusak), itu tidak akan memberi Anda kinerja yang lebih baik. Biasanya tidak ada kerugian besar, karena string 1 atau 2 huruf hampir tidak selektif (lebih dari beberapa persen kecocokan tabel yang mendasarinya) dan dukungan indeks tidak akan meningkatkan kinerja untuk memulai, karena pemindaian tabel lengkap lebih cepat.


text_pattern_ops untuk pencocokan awalan

Untuk pola jangkar kiri saja (tanpa wildcard terkemuka) Anda mendapatkan yang optimal dengan kelas operator yang sesuai untuk indeks btree: text_pattern_opsatau varchar_pattern_ops. Kedua fitur bawaan Postgres standar, tidak diperlukan modul tambahan. Performa serupa, tetapi indeks jauh lebih kecil.

Contoh indeks:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

Contoh kueri:

SELECT * FROM tbl WHERE col LIKE 'foo%';  -- no leading wildcard

Atau , jika Anda harus menjalankan database Anda dengan lokal 'C' (secara efektif tidak ada lokal), maka semuanya akan disortir menurut urutan byte dan indeks btree biasa dengan kelas operator default melakukan tugasnya.

Lebih detail, penjelasan, contoh dan tautan dalam jawaban terkait ini di dba.SE:


Tanpa karakter pengganti utama pada tabel 500 ribu baris, indeks gin dengan gin_trgm_ops tampak 10 kali lebih cepat daripada btree
nicolas

@nicolas: Perbandingan bergantung pada banyak variabel. Panjang kunci, distribusi data, panjang pola, kemungkinan pemindaian hanya indeks ... Dan yang terpenting: versi Postgres. Indeks GIN telah meningkat secara substansial di halaman 9.4 dan 9.5. Versi baru pg_trgm (akan dirilis dengan pg 9.6) akan membawa lebih banyak perbaikan.
Erwin Brandstetter

1
Jika saya mendapatkan dokumen yang benar, dengan pg_trgmAnda memerlukan string kueri yang panjangnya minimal 3 karakter, misalnya fo%tidak akan mengenai indeks tetapi melakukan pemindaian sebagai gantinya. Sesuatu untuk diperhatikan.
Tuukka Mustonen

1
@TuukkaMustonen: Poin bagus. Pemindaian indeks (bitmap) masih berfungsi , hanya saja tidak akan memberi Anda kinerja yang lebih baik. Saya menambahkan beberapa klarifikasi di atas.
Erwin Brandstetter

7

Mungkin yang cepat adalah pola berlabuh dengan case-sensitive seperti itu dapat menggunakan indeks. yaitu tidak ada kartu bebas di awal string pertandingan sehingga pelaksana dapat menggunakan pemindaian rentang indeks. ( komentar relevan di dokumen ada di sini ) Lower dan ilike juga akan kehilangan kemampuan Anda untuk menggunakan indeks kecuali Anda secara khusus membuat indeks untuk tujuan itu (lihat indeks fungsional ).

Jika Anda ingin mencari string di tengah bidang, Anda harus melihat ke dalam indeks teks lengkap atau trigram . Yang pertama ada di inti Postgres, yang lainnya tersedia di modul kontrib.


Saya tidak berpikir untuk membuat indeks pada nilai huruf kecil dari bidang tersebut. Dengan cara itu saya bisa mengonversi teks kueri menjadi huruf kecil di backend sebelum membuat kueri.
Jason

4

Anda dapat menginstal Wildspeed , jenis indeks yang berbeda di PostgreSQL. Wildspeed berfungsi dengan% word% wildcard, tidak masalah. Sisi negatifnya adalah ukuran indeks, ini bisa besar, sangat besar.


3

Silakan Jalankan kueri yang disebutkan di bawah ini untuk meningkatkan kinerja kueri LIKE di postgresql. buat indeks seperti ini untuk tabel yang lebih besar:

CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)

Ini hanya berfungsi jika pola tidak dimulai dengan karakter pengganti - dalam hal ini, dua kueri sampel pertama dimulai dengan karakter pengganti.
cbz


1

Saya baru-baru ini mengalami masalah serupa dengan tabel yang berisi 200000 catatan dan saya perlu melakukan kueri LIKE berulang kali. Dalam kasus saya, string yang sedang dicari sudah diperbaiki. Bidang lainnya bervariasi. Karena itu, saya bisa menulis ulang:

SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');

sebagai

CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));

SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;

Saya senang ketika kueri kembali dengan cepat dan memverifikasi bahwa indeks digunakan dengan EXPLAIN ANALYZE:

 Bitmap Heap Scan on parcels  (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
   Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
   ->  Bitmap Index Scan on ix_parcels  (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
         Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
 Planning time: 0.075 ms
 Execution time: 0.025 ms

0

Kueri suka Anda mungkin tidak dapat menggunakan indeks yang Anda buat karena:

1) kriteria LIKE Anda dimulai dengan wildcard.

2) Anda telah menggunakan fungsi dengan kriteria LIKE Anda.


0

Kapanpun Anda menggunakan klausa pada kolom dengan fungsi seperti LIKE, ILIKE, upper, lower dll. Kemudian postgres tidak akan mempertimbangkan indeks normal Anda. Ini akan melakukan pemindaian penuh dari tabel melalui setiap baris dan oleh karena itu akan lambat.

Cara yang benar adalah dengan membuat indeks baru sesuai dengan kueri Anda. Misalnya jika saya ingin mencocokkan kolom tanpa sensitivitas huruf dan kolom saya adalah varchar. Kemudian Anda bisa melakukannya seperti ini.

create index ix_tblname_col_upper on tblname (UPPER(col) varchar_pattern_ops);

Demikian pula jika kolom Anda adalah teks maka Anda melakukan sesuatu seperti ini

create index ix_tblname_col_upper on tblname (UPPER(col) text_pattern_ops);

Demikian pula Anda dapat mengubah fungsi atas ke fungsi lain yang Anda inginkan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.