Bir ifadede toplu bir LIKE sorgusunu hızlandırmak için bir dizin nasıl oluşturulur?


20

Başlıkta yanlış soru soruyor olabilirim. İşte gerçekler:

Müşteri hizmetleri halkım, Django tabanlı sitemizin yönetim arayüzünde müşteri aramaları yaparken yavaş yanıt sürelerinden şikayet ediyor.

Postgres 8.4.6 kullanıyoruz. Yavaş sorguları kaydetmeye başladım ve bu suçluyu keşfettim:

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

Bu sorgunun çalışması 32 saniyeden fazla sürüyor. EXPLAIN tarafından sağlanan sorgu planı:

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)

Bu Django Yönetici uygulaması tarafından oluşturulan bir Django QuerySet Django ORM tarafından oluşturulan bir sorgu olduğundan, sorgu kendisi üzerinde herhangi bir denetimim yok. Bir dizin mantıklı bir çözüm gibi görünüyor. Bunu hızlandırmak için bir dizin oluşturmayı denedim, ancak bir fark yaratmadı:

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

Neyi yanlış yapıyorum? Bu sorguyu nasıl hızlandırabilirim?

Yanıtlar:


21

PostgreSQL 8.4'teLIKE / ILIKEiçinde , sol bağlantılı arama terimleri dışında hiçbir dizin desteği yoktur .

PostgreSQL 9.1'den bu yana , ek modül pg_trgmGIN ve GiST trigram indeksleri için operatör sınıflarını destekler LIKE/ / ILIKEdüzenli ifadeleri (operatörler ~ve arkadaşlar). Veritabanı başına bir kez kurun:

CREATE EXTENSION pg_trgm;

Örnek GIN endeksi:

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

İlişkili:


2
Bu aslında doğru cevap.
vonPetrushev

9

Bu dizin, eşleşmenizin başındaki '%' nedeniyle yardımcı olmaz - BTREE dizini yalnızca öneklerle eşleşebilir ve sorgunuzun başındaki joker karakter, aranacak sabit bir önek olmadığı anlamına gelir.

Bu yüzden bir tablo taraması yapıyor ve her kaydı sırayla sorgu dizesiyle eşleştiriyor.

Muhtemelen şu anda bulunduğunuz LIKE ile alt dize araması yapmak yerine tam metin dizini ve metin eşleştirme işleçleri kullanmanız gerekir. Tam metin araması hakkında daha fazla bilgiyi belgelerde bulabilirsiniz:

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

Aslında o sayfadan LIKE'nin görünüşte hiçbir zaman endeksleri kullanmadığını fark ettim, bu da BTREE dizini kullanarak joker olmayan önekleri çözebilmesi gerektiği için bana garip geliyor. Birkaç hızlı test, belgelerin muhtemelen doğru olduğunu gösterir, bu durumda sorguyu çözmek için LIKE kullanırken hiçbir endeksleme yardımcı olmaz.


Ben de bundan korkuyordum. Yardımcı olacak başka bir dizin türü var mı? Dediğim gibi, sorgunun kendisini etkileme yeteneğim biraz kısıtlı.
David Eyk

Ayrıca, öncü %gerekli bir özelliktir: müşteri hizmetleri temsilcileri, özellikle e-posta adresinde bir yazım hatası olduğunda müşteri hesaplarını bulmak için ona ihtiyaç duyar.
David Eyk

LIKE ve tam metin dizine ekleme ile ilgili biraz araştırma yaptıktan sonra, amacınızı görmeye başlıyorum.
David Eyk

Şimdilik, önde gelen joker karakteri bastırmanın bir yolunu buldum. Size çıkıyor olabilir Eğer uygun bir ile bir dizin oluşturursanız, LIKE ile bir dizin kullanmak operatör sınıfında . Dokümanlar burada: postgresql.org/docs/8.4/static/indexes-opclass.html
David Eyk

Ayrıca, şişkinlik için db'nizi kontrol edin. Bu tabloda çok fazla şişkinlik varsa, taramak için uzun zaman alacaktır. Biraz boş zamanınız varsa, birincil anahtarda kümeleyin ve daha hızlı olup olmadığını görün. Şişkinliği kontrol etmek istiyorsanız, analiz çalıştırabilir ve ardından sorguyu burada çalıştırabilirsiniz: wiki.postgresql.org/wiki/Show_database_bloat . Daha doğru değerler için bu sayfanın altına bakın.
Scott Marlowe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.