PostgreSQL LIKE sorgu performans varyasyonları


113

LIKEVeritabanımdaki belirli bir tabloya yönelik sorgulara ilişkin yanıt sürelerinde oldukça büyük bir değişiklik görüyorum . Bazen sonuçları 200-400 ms içinde alırım (çok kabul edilebilir), ancak diğer zamanlarda sonuçların döndürülmesi 30 saniye kadar sürebilir.

LIKESorguların çok yoğun kaynak gerektirdiğini anlıyorum, ancak yanıt sürelerinde neden bu kadar büyük bir fark olduğunu anlamıyorum. Sahada bir btree indeksi oluşturdum owner1ama LIKEsorgulara yardımcı olduğunu düşünmüyorum . Herhangi bir fikri olan var mı?

Örnek SQL:

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

Ben de denedim:

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

Ve:

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

Benzer sonuçlarla.
Tablo Satır Sayısı: yaklaşık 95.000.

Yanıtlar:


284

FTS desteklemiyor LIKE

Daha önce kabul cevap yanlış. Tam Metin Arama tam metin dizinleri ile değil için LIKEkendi operatörleri vardır ve keyfi dizeleri için çalışmaz, hiç operatör. Sözlüklere ve kök oluşturmaya dayalı sözcükler üzerinde çalışır . Bu does destek kelimeler için önek eşleşmesini ile değil, LIKEoperatör:

Trigram dizinleri LIKE

Yalnızca sola bağlı olanları değil tüm modelleri ve modelleri desteklemek pg_trgmiçin GIN ve GiST trigram dizinleri için operatör sınıfları sağlayan ek modülü kurun :LIKEILIKE

Örnek dizin:

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

Veya:

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

Örnek sorgu:

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

Trigram? Daha kısa dizeler ne olacak?

Dizine alınmış değerlerde 3'ten az harf içeren sözcükler hala çalışır. Kullanım kılavuzu:

Dizede bulunan trigram kümesini belirlerken, her sözcüğün önünde iki boşluğa ve son eklenmiş bir boşluğa sahip olduğu kabul edilir.

Ve 3 harften az arama kalıpları? Kullanım kılavuzu:

Hem LIKEve hem de normal ifade aramaları için, çıkarılabilir trigramları olmayan bir modelin tam dizin taramasına dönüşeceğini unutmayın.

Yani, bu dizin / bitmap dizini taramaları hala çalışır (hazırlanmış deyim için sorgu planları bozulmaz), size daha iyi performans sağlamaz. Tipik olarak büyük bir kayıp olmaz, çünkü 1 veya 2 harfli dizeler pek seçici değildir (temel tablo eşleşmelerinin yüzde birkaçından fazlası) ve dizin desteği, başlangıçta performansı iyileştirmez, çünkü tam bir tablo taraması daha hızlıdır.


text_pattern_ops önek eşleşmesi için

Yalnızca sola bağlantılı desenler için (önde gelen joker karakter yok) , bir btree dizini için uygun bir operatör sınıfıyla optimum olanı elde edersiniz: text_pattern_opsveya varchar_pattern_ops. Standart Postgres'in her iki yerleşik özelliği de ek modül gerekmez. Benzer performans, ancak çok daha küçük indeks.

Örnek dizin:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

Örnek sorgu:

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

Veya , veritabanınızı 'C' yerel ayarıyla çalıştırmanız gerekiyorsa (etkin bir şekilde yerel ayar yok ), o zaman her şey yine de bayt sırasına göre sıralanır ve varsayılan operatör sınıfına sahip düz bir btree dizini işi yapar.

Dba.SE'deki bu ilgili cevaplarda daha fazla ayrıntı, açıklama, örnekler ve bağlantılar:


500 bin satırlık bir tabloda önde gelen joker karakter olmadan, gin_trgm_ops ile gin indeksi btree'den 10 kat daha hızlı görünüyor
nicolas

@nicolas: Karşılaştırma birçok değişkene bağlıdır. Anahtar uzunluğu, veri dağılımı, kalıp uzunluğu, yalnızca olası dizin taraması ... Ve en önemlisi: Postgres sürümü. GIN indeksleri, sayfa 9.4 ve 9.5'te önemli ölçüde iyileştirilmiştir. Pg_trgm'nin yeni bir sürümü (sayfa 9.6 ile piyasaya sürülecek) daha fazla iyileştirme getirecek.
Erwin Brandstetter

1
Dokümanları doğru pg_trgmanladıysam, en az 3 karakter uzunluğunda bir sorgu dizesine ihtiyacınız varsa, örneğin fo%dizine basmaz, bunun yerine bir tarama yapar. Dikkat edilecek bir şey.
Tuukka Mustonen

1
@TuukkaMustonen: İyi nokta. Eh, (bitmap) dizin taramaları hala çalışıyor , size daha iyi performans satın almayacaklar. Yukarıya biraz açıklama ekledim.
Erwin Brandstetter

7

Muhtemelen hızlı olanlar, indeksleri kullanabilen büyük / küçük harfe duyarlı olan bağlantılı modellerdir. yani, eşleme dizesinin başında joker karakter yoktur, bu nedenle uygulayıcı bir dizin aralığı taraması kullanabilir. ( Dokümanlardaki ilgili yorum buradadır ) Aşağı ve ilike, bu amaç için özel olarak bir dizin oluşturmadığınız sürece, dizini kullanma yeteneğinizi de kaybedecektir ( işlevsel dizinlere bakın ).

Alanın ortasında dize aramak istiyorsanız, tam metin veya trigram dizinlerine bakmalısınız . Birincisi Postgres çekirdeğinde, diğeri katkı modüllerinde mevcut.


Alanın küçük harfli değerinde bir dizin oluşturmayı düşünmemiştim. Bu şekilde sorgu metnini sorgulamadan önce arka uçta küçük harfe dönüştürebilirim.
Jason

4

PostgreSQL'de farklı bir indeks türü olan Wildspeed'i kurabilirsiniz . Wildspeed,% word% joker karakterlerle çalışıyor, sorun değil. Olumsuz tarafı endeksin boyutu, bu çok büyük olabilir.


3

Postgresql'de LIKE sorgu performansını iyileştirmek için lütfen aşağıda belirtilen sorguyu çalıştırın. daha büyük tablolar için bunun gibi bir dizin oluşturun:

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

Bu, yalnızca desen bir joker karakterle başlamazsa işe yarar - bu durumda ilk iki örnek sorgu, her ikisi de bir joker karakterle başlar.
cbz


1

Yakın zamanda 200000 kayıt içeren bir tabloyla benzer bir sorun yaşadım ve tekrarlanan LIKE sorguları yapmam gerekiyor. Benim durumumda, aranan dize düzeltildi. Diğer alanlar değişiyordu. Bu yüzden şunu yeniden yazabildim:

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

gibi

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;

Sorgular hızlı bir şekilde geri geldiğinde ve dizinin şu şekilde kullanıldığını doğruladığında çok sevindim 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

Benzer sorgularınız muhtemelen oluşturduğunuz dizinleri kullanamaz çünkü:

1) LIKE kriterleriniz bir joker karakterle başlar.

2) LIKE kriterlerinizle bir işlev kullandınız.


0

Ne zaman bir sütun üzerinde LIKE, ILIKE, Upper, Lower vb. Gibi işlevlere sahip bir cümle kullandığınızda, postgres normal indeksinizi dikkate almayacaktır. Her satırdan geçen tablonun tam bir taramasını yapacak ve bu nedenle yavaş olacaktır.

Doğru yol, sorgunuza göre yeni bir dizin oluşturmaktır. Örneğin, büyük / küçük harf duyarlılığı olmayan bir sütunu eşleştirmek istiyorsam ve sütunum bir varchar ise. O zaman bunu böyle yapabilirsin.

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

Benzer şekilde, sütununuz bir metinse, böyle bir şey yaparsınız

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

Benzer şekilde, üstteki işlevi istediğiniz başka bir işleve değiştirebilirsiniz.

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.