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

Heap erişimini önlemek ve index-only scan almak için covering index'e (INCLUDE) ne zaman geçmeliyim?


Soru

PostgreSQL'de sık çalışan bir lookup sorgum var: `WHERE customer_id = $1` ile filtreleyip `status` ve `total` kolonlarını okuyorum. `customer_id` üzerinde bir B-tree index'im zaten mevcut ve index bu satırları buluyor. Sorun şu ki EXPLAIN çıktısında hâlâ bir heap erişimi görüyorum: index satırı bulduktan sonra `status` ve `total`'ı almak için tabloya (heap) gidiyor. Bu iki fazladan kolonu heap'e gitmeden almak için `INCLUDE` ile covering index'e ne zaman geçmeliyim, geçince gerçekten index-only scan alır mıyım?

Cevap

Kısa cevap: Sorgu gerçekten sıcaksa (yüksek frekans, düşük gecikme beklentisi) ve heap erişimi ölçülmüş bir maliyetse geçin.

Kısa cevap

Ama bir uyarı var: covering index tek başına index-only scan garantisi vermez — bu, tablonun visibility map durumuna bağlıdır.

Neden

  1. INCLUDE, kolonları yalnızca leaf sayfalara koyar. INCLUDE (status, total) bu kolonları B-tree’nin sıralama anahtarına eklemez; yani karşılaştırma maliyeti getirmezler ama sorguyu heap’e gitmeden karşılamak için orada dururlar. Amacınız tam olarak bu.

  2. Index-only scan visibility map’e bağlıdır. Postgres heap’i yalnızca sayfa “all-visible” olarak işaretliyse atlar. Yazma yoğun bir tabloda bu harita vacuum’lar arasında bayatlar; o zaman covering index olsa bile EXPLAIN’de Heap Fetches: N görürsünüz. Yani asıl kaldıraç bazen index değil, autovacuum’u sıkılaştırmaktır.

    CREATE INDEX idx_orders_customer
      ON orders (customer_id)
      INCLUDE (status, total);
    
    EXPLAIN (ANALYZE, BUFFERS)
    SELECT status, total FROM orders WHERE customer_id = $1;
    -- Hedef: "Index Only Scan ... Heap Fetches: 0"
  3. Bu bir yazma/alan takasıdır. Daha geniş leaf sayfaları = daha büyük index, daha fazla WAL, daha yavaş yazma, cache’te tutulacak daha çok veri. Bu maliyeti yalnızca gerçekten sıcak okumalar hak eder; her sorgu için covering index dökmeyin.

  4. INCLUDE kolonları yalnızca projeksiyon içindir. Bunu unutmayın: INCLUDE ile eklenen kolonlar WHERE filtresinde ya da ORDER BY’da kullanılamaz, sadece SELECT çıktısını karşılamak için oradadır. Eğer o kolon üzerinde de filtreleyecekseniz onu anahtara (belki bileşik index’e) koymanız gerekir; ihtiyacınız sadece “geri döndürmek” ise INCLUDE doğru yerdir.

Ne yapmalı

  1. Kolonu anahtara eklemektense INCLUDE’u tercih edin. status, total’ı index anahtarına koymak iç düğümleri şişirebilir ve ihtiyacınız olmayan bir sıralama dayatır. INCLUDE ağacı ince tutar, sadece leaf’i genişletir — sorguyu karşılamak için ihtiyacınız olan tam da bu.

  2. EXPLAIN (ANALYZE, BUFFERS) ile doğrulayın. “Index Only Scan” ve “Heap Fetches: 0” görene kadar iş bitmiş sayılmaz. Heap fetch yüksek kalıyorsa çözüm daha fazla index değil, VACUUM (ya da autovacuum eşiklerini düşürmek) olabilir. İndex’in gerçekten kullanılıp kullanılmadığını pg_stat_user_indexes ile takip edin; kimsenin dokunmadığı bir covering index sadece yazma yükü demektir.

Sonuç: Ben olsam önce EXPLAIN (ANALYZE, BUFFERS) ile heap erişiminin gerçekten baskın maliyet olduğunu doğrular, sonra customer_id index’ini INCLUDE (status, total) ile yeniden oluşturur ve Heap Fetches: 0 görene kadar autovacuum’u bu tablo için sıkılaştırırdım. Zaman içinde index’in şiştiğini görürseniz REINDEX CONCURRENTLY ile toparlayın. Kısacası: bu iki kolon gerçekten sıcak bir sorgunun read-only yükü ise covering index kazandırır; değilse ölçmeden index eklemek çoğu zaman sadece yazma yolunu yavaşlatır.

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