PostgreSQL tam metin arama: Doğru yapılırsa hızlıdır (yavaş olduğu yanılgısını giderme)
(blog.vectorchord.ai)- PostgreSQL’in yerleşik Full-Text Search(FTS) özelliğinin yavaş olduğuna dair bir algı var, ancak uygun optimizasyon yapıldığında son derece hızlı çalışabilir
- Neon’un blogunda Rust tabanlı
pg_searchuzantısı ile yerleşik FTS karşılaştırılıyor ve ikincisinin yavaş olduğu öne sürülüyor - Ancak bu karşılaştırma büyük olasılıkla PostgreSQL FTS için gerekli olan temel optimizasyon adımları atlanmış halde yapıldı
- Bu yazı, yerleşik FTS yapılandırmasına yalnızca basit optimizasyonlar uygulanarak bile 50 kat performans artışı sağlanabileceğini sayılarla gösteriyor
Benchmark kurulumuna genel bakış
- 10 milyon log verisi içeren bir tablo temel alınarak test yapıldı
CREATE TABLE benchmark_logs ( id SERIAL PRIMARY KEY, message TEXT, country VARCHAR(255), severity INTEGER, timestamp TIMESTAMP, metadata JSONB ); - Sorunlu sorgu yapısı:
SELECT country, COUNT(*) FROM benchmark_logs WHERE to_tsvector('english', message) @@ to_tsquery('english', 'research') GROUP BY country ORDER BY country;to_tsvector()sorgu içinde çalıştırılıyor → son derece verimsiz- GIN indeksi olsa bile düzgün şekilde kullanılamıyor
Test ortamı (varsayılan kurulumun kopyası)
- EC2 i7ie.xlarge instance, yerel NVMe SSD kullanıldı
- 4 vCPU, PostgreSQL 16(Docker) kullanıldı
- Başlıca PostgreSQL ayarları:
-c shared_buffers=8GB -c maintenance_work_mem=8GB -c max_parallel_workers=4 -c max_worker_processes=4 - Paralel işleme sınırı: max_parallel_workers_per_gather = 2 (Neon 8 kullanıyor)
Performans düşüşü nedeni 1: anlık tsvector hesaplama
to_tsvector()sorgu içinde çalıştırıldığında:- metin ayrıştırma, morfolojik analiz vb. her seferinde yeniden yapılır
- indekslerden hiç yararlanılamaz
-
Çözüm:
tsvectorsütununu önceden oluşturup indekslemek- 1.
tsvectorsütunu ekleyin
ALTER TABLE benchmark_logs ADD COLUMN message_tsvector tsvector;- 2. Veriyi doldurun
UPDATE benchmark_logs SET message_tsvector = to_tsvector('english', message); - 3. İndeks oluşturun (
fastupdatedevre dışı)CREATE INDEX idx_gin_logs_message_tsvector ON benchmark_logs USING GIN (message_tsvector) WITH (fastupdate = off); - 4. Sorguyu değiştirin
SELECT country, COUNT(*) FROM benchmark_logs WHERE message_tsvector @@ to_tsquery('english', 'research') GROUP BY country ORDER BY country;
- 1.
Performans düşüşü nedeni 2: GIN indeksinde fastupdate=on ayarı
fastupdate=onyazma performansı için faydalıdır, ancak arama performansını olumsuz etkiler- Salt okunur veya arama odaklı veri kümelerinde
fastupdate=offzorunludur - İndeks daha küçük ve daha hızlı olur, ayrıca pending list işleme ihtiyacı ortadan kalkar
-
Optimize edilmiş GIN indeksi oluşturma yöntemi
CREATE INDEX idx_gin_logs_message_tsvector ON benchmark_logs USING GIN (message_tsvector) WITH (fastupdate = off);
Performans artışı: 50 kattan fazla iyileşme
- Optimizasyon öncesi: yaklaşık 41,3 saniye (41.301 ms)
- Optimizasyon sonrası: yaklaşık 0,88 saniye (877 ms)
- Yaklaşık 50 kat performans artışı gösteriyor
- Daha az paralel işleme kullanılan ortamlarda da bu performans elde edilebiliyor
ts_rank performansı gerçekten yavaş olabilir
ts_rankveyats_rank_cd, tüm sonuçları değerlendirip ardından sıraladığı için görece yavaş olabilir- Özellikle çok sayıda sonuç söz konusu olduğunda CPU/IO yükü büyür
Gelişmiş sıralama özelliği: VectorChord-BM25 uzantısı
- Sıralama doğruluğu ve hızının önemli olduğu durumlarda özel bir uzantı kullanmak daha etkili olabilir
- VectorChord-BM25, PostgreSQL için bir uzantıdır ve BM25 algoritması tabanlı sıralama değerlendirmesi sunar
- Elasticsearch’ten 3 kat daha hızlı olduğuna dair raporlar da bulunuyor
VectorChord-BM25’in avantajları
- BM25 algoritması: TF-IDF’den daha gelişmiş bir arama sıralama algoritması
- Özel indeks biçimi: Block WeakAnd gibi yüksek hızlı arama optimizasyonları
bm25vectortipi sunar: tokenize edilmiş gösterimi saklar- Hem arama doğruluğunu hem de hızı artırır
Sonuç: PostgreSQL’in yerleşik FTS’i de fazlasıyla hızlı olabilir
tsvectorsütunu ve uygun GIN indeksi (fastupdate=off) kullanıldığında, yerleşik FTS ile de çok hızlı arama yapılabilir- Performans karşılaştırmaları optimize edilmiş bir temel üzerinden yapılmalıdır
- Gelişmiş sıralama özelliklerine ihtiyaç varsa VectorChord-BM25 gibi uzantı araçları değerlendirilebilir
- Ana mesaj: yavaş olan araç değil, sorun ayarlarda olabilir
3 yorum
Sayesinde sorgu ayarı yaptım.
Hacker News yorumları korkutucu... "On milyon? Şaka mı?"
Hacker News görüşleri
pg_search'ün bakımcısı olarak, Postgres belgelerine göre hem Neon/ParadeDB yazısında hem de burada kullanılan stratejilerin geçerli alternatifler olarak sunulduğunu belirtiyorum
tsvector'ü gerçek zamanlı hesaplamak büyük bir hataHer şeyi Postgres'in içine koyma eğilimini anlayamıyorum
Postgres-native tam metin arama uygulamalarını daha fazla görmek hoşuma gidiyor
Açıklama planı olmadığı için neler olduğunu anlamak zor
tsvectoryeniden denetimi yalnızca eşleşen kayıtlar için uygulanır ve benchmark sorgusuLIMIT 10kullandığı için yeniden denetim az olurginindex üzerinde şartları var; bu yüzden planlayıcı önce tüm eşleşmeleri yeniden denetliyor gibi görünüyorBirkaç yıl önce native FTS kullanmak istemiştim ama başaramadım
pg_search ve vchord_bm25 eklentileri için RPM/DEB paketleri hazırladım
Birçok ekibin doğrudan Elasticsearch veya Meilisearch'e geçtiğini gördüm
10 milyon kayıt oyuncak bir veri kümesi
İlk kez 2008 civarında pg tam metnini kullandım