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
-
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. -
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: Ngö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" -
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.
-
INCLUDE kolonları yalnızca projeksiyon içindir. Bunu unutmayın:
INCLUDEile eklenen kolonlarWHEREfiltresinde ya daORDER BY’da kullanılamaz, sadeceSELECTçı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ı
-
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. -
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_indexesile 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.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.