İçeriğe geç
Muhammet Şafak
en
Soran: Yiğit Cevaplandı:

Sadece 'pending' satırları taranan bir kuyruk tablosunda partial index kullanmalı mıyım?


Soru

Postgres'te bir `jobs` kuyruk tablom var. İçinde milyonlarca `completed` satır birikiyor ama ben yalnızca birkaç bin `pending` satırı sorguluyorum: worker'lar `WHERE status = 'pending' ORDER BY created_at` ile sıradaki işi çekiyor. `status` üzerine normal bir index mi, yoksa `WHERE status = 'pending'` koşullu bir partial index mi kullanmalıyım? Partial index'i kullanırken planner'ın onu gerçekten seçmesi için nelere dikkat etmeliyim?

Cevap

Kısa cevap: Evet, partial index tam da bu senaryonun ders kitabı örneğidir. WHERE status = 'pending' koşullu bir index yalnızca birkaç bin satırı tutar; küçük olur, belleğe sığar, taraması hızlı ve güncellemesi ucuzdur — milyonlarca ölü satıra hiç dokunmadan geçer.

  1. Partial index yalnızca eşleşen satırları indeksler. CREATE INDEX ... WHERE status='pending' ile index milyonlarca değil birkaç bin girdi tutar. Bu, onu neredeyse tamamen cache’te tutar ve her insert/update’te güncellenmesi gereken ağacı küçültür.

  2. Planner’ın predicate’i eşleştirmesi gerekir. Sorgunuzun WHERE’i, index predicate’inin (status='pending') mantıksal olarak kapsadığı bir koşul olmalı. status = $1 gibi parametreyle sorarsanız planner index’i seçemeyebilir; koşulu literal 'pending' olarak yazın ki eşleşme kanıtlanabilsin.

  3. ORDER BY kolonunu index’e koyun. Worker’lar tipik olarak WHERE status='pending' ORDER BY created_at FOR UPDATE SKIP LOCKED yapar. Sıralama anahtarını index’e dahil edin: (created_at) WHERE status='pending'. Böylece hem index sırasında tarama alırsınız hem de SKIP LOCKED ile kilitli satırları atlayıp sıradaki işi anında çekersiniz.

  4. Churn ve bloat’a dikkat. Bir satır pending’den done’a döndüğünde partial index’ten düşer — bu iyi. Ama yoğun update trafiği bloat üretebilir; autovacuum’un sağlıklı çalışması önemli. İyi haber: index küçük olduğu için vacuum’u da ucuzdur.

  5. Status üzerine düz index’ten kesinlikle daha iyidir. Düşük kardinaliteli status kolonuna düz bir index’in büyük kısmı işe yaramaz done değerleridir; planner çoğu zaman onu es geçip seq scan yapar. Bu erişim deseni için partial index net üstündür.

  6. Aynı mantık her çarpık predicate için geçerlidir. Ders yalnızca kuyruklara özgü değil: WHERE deleted_at IS NULL (soft-delete), WHERE processed = false gibi tablonun küçük bir azınlığını hedefleyen her sorguda partial index aynı kazancı verir. “Az sayıda satırı, çok sayıda ölü satırın arasından” çektiğiniz her yerde aklınıza gelsin.

SQL şöyle:

CREATE INDEX idx_jobs_pending
    ON jobs (created_at)
    WHERE status = 'pending';

-- worker'ın çekiş sorgusu:
SELECT id, payload
FROM jobs
WHERE status = 'pending'
ORDER BY created_at
FOR UPDATE SKIP LOCKED
LIMIT 1;

Sonuç: Ben olsam çekiş predicate’i üzerine, ORDER BY kolonunu da içerecek şekilde bir partial index kurar ve onu FOR UPDATE SKIP LOCKED ile eşlerdim. Tek dikkat noktası: sorgularınızın 'pending'’i literal geçmesi ki planner index’i gerçekten seçsin. EXPLAIN (ANALYZE, BUFFERS) ile index scan aldığınızı bir kez doğrulayın; sonra milyonlarca completed satırın birikmesine rahatça aldırmazsınız.

Etiketler: #postgresql#index#performans
Paylaş:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

Diğer Sorular

Tüm sorular

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi