Bagaimana cara membuat indeks untuk mempercepat permintaan LIKE agregat pada ekspresi?


20

Saya mungkin mengajukan pertanyaan yang salah dalam judul. Berikut ini faktanya:

Masyarakat layanan pelanggan saya mengeluh tentang waktu respons yang lambat ketika melakukan pencarian pelanggan pada antarmuka administrasi situs berbasis Django kami.

Kami menggunakan Postgres 8.4.6. Saya mulai mencatat pertanyaan yang lambat, dan menemukan penyebabnya:

SELECT COUNT(*) FROM "auth_user" WHERE UPPER("auth_user"."email"::text) LIKE UPPER(E'%deyk%')

Kueri ini membutuhkan waktu hingga 32 detik untuk dijalankan. Inilah paket kueri yang disediakan oleh EXPLAIN:

QUERY PLAN
Aggregate  (cost=205171.71..205171.72 rows=1 width=0)
  ->  Seq Scan on auth_user  (cost=0.00..205166.46 rows=2096 width=0)
        Filter: (upper((email)::text) ~~ '%DEYK%'::text)

Karena ini adalah kueri yang dihasilkan oleh Django ORM dari Django QuerySet yang dihasilkan oleh aplikasi Admin Django, saya tidak memiliki kontrol atas kueri itu sendiri. Indeks sepertinya solusi logis. Saya mencoba membuat indeks untuk mempercepat ini, tetapi tidak ada bedanya:

CREATE INDEX auth_user_email_upper ON auth_user USING btree (upper(email::text))

Apa yang saya lakukan salah? Bagaimana saya bisa mempercepat permintaan ini?

Jawaban:


21

Tidak ada dukungan indeks untuk LIKE/ ILIKEdi PostgreSQL 8.4 - kecuali untuk istilah pencarian berlabuh kiri .

Sejak PostgreSQL 9.1 modul tambahan pg_trgmmenyediakan kelas operator untuk indeks trigram GIN dan GiST yang mendukung LIKE/ ILIKEatau ekspresi reguler (operator ~dan teman). Instal sekali per basis data:

CREATE EXTENSION pg_trgm;

Contoh indeks GIN:

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

Terkait:


2
Ini sebenarnya jawaban yang benar.
vonPetrushev

9

Indeks itu tidak akan membantu karena '%' di awal pertandingan Anda - indeks BTREE hanya dapat mencocokkan awalan dan wildcard di awal kueri Anda berarti tidak ada awalan tetap yang harus dicari.

Itu sebabnya ia melakukan pemindaian tabel dan mencocokkan setiap catatan pada gilirannya dengan string kueri.

Anda mungkin perlu melihat menggunakan indeks teks lengkap dan operator pencocokan teks daripada melakukan pencarian substring dengan SEPERTI bahwa Anda saat ini. Anda dapat menemukan lebih banyak tentang pencarian teks lengkap dalam dokumentasi:

http://www.postgresql.org/docs/8.4/static/textsearch-intro.html

Bahkan saya perhatikan dari halaman itu bahwa LIKE sepertinya tidak pernah menggunakan indeks, yang menurut saya aneh karena seharusnya bisa menyelesaikan awalan non-wildcard menggunakan indeks BTREE. Namun, beberapa tes cepat menunjukkan bahwa dokumentasi mungkin benar, dalam hal ini tidak ada jumlah pengindeksan yang akan membantu saat Anda menggunakan LIKE untuk menyelesaikan kueri.


Itu yang saya takutkan. Apakah ada jenis indeks lain yang akan membantu? Seperti yang saya katakan, saya sedikit terkendala dalam kemampuan saya untuk mempengaruhi permintaan itu sendiri.
David Eyk

Juga, yang utama %adalah fitur yang diperlukan: perwakilan layanan pelanggan membutuhkannya untuk menemukan akun pelanggan, terutama ketika ada salah ketik di alamat email.
David Eyk

Nah, setelah sedikit riset tentang LIKE dan pengindeksan teks lengkap, dan saya mulai mengerti maksud Anda.
David Eyk

Untuk saat ini, saya telah menemukan cara untuk menekan wildcard terkemuka. Ternyata Anda dapat menggunakan indeks dengan LIKE, jika Anda membuat indeks dengan kelas operator yang sesuai . Documents ada di sini: postgresql.org/docs/8.4/static/indexes-opclass.html
David Eyk

Juga, periksa db Anda untuk mengasapi. Jika Anda punya banyak mengasapi di meja itu, akan butuh waktu lama untuk seq memindai. Jika Anda memiliki waktu henti, cukup mengelompokkannya pada kunci utama dan melihat apakah itu menjadi lebih cepat. Jika Anda ingin memeriksa bloat, Anda dapat menjalankan analisis lalu jalankan kueri di sini: wiki.postgresql.org/wiki/Show_database_bloat . Untuk nilai yang lebih akurat, lihat bagian bawah halaman itu.
Scott Marlowe
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.