Postgres'i, aksi halde sıralı bir tarama yapmakta ısrar edecekse bir dizini kullanmaya nasıl zorlarım?
Postgres'i, aksi halde sıralı bir tarama yapmakta ısrar edecekse bir dizini kullanmaya nasıl zorlarım?
Yanıtlar:
Pek çok veritabanında bulunan ortak "dizin ipucu" özelliğini sorduğunuzu varsayarsak, PostgreSQL böyle bir özellik sağlamaz. Bu, PostgreSQL ekibi tarafından verilen bilinçli bir karardı. Bunun yerine neden ve ne yapabileceğinize dair iyi bir genel bakış burada bulunabilir . Bunun nedeni, temelde, verileriniz değiştikçe daha sonra daha fazla soruna yol açma eğiliminde olan bir performans saldırısıdır, oysa PostgreSQL'in optimize edicisi, istatistikleri temel alarak planı yeniden değerlendirebilir. Başka bir deyişle, bugün iyi bir sorgu planı olabilecek şey, muhtemelen her zaman için iyi bir sorgu planı olmayacaktır ve dizin ipuçları, her zaman için belirli bir sorgu planını zorunlu kılar.
Çok kör bir çekiç olarak test etmek için yararlıdır, enable_seqscan
ve enable_indexscan
parametrelerini kullanabilirsiniz . Görmek:
Bunlar sürekli üretim kullanımı için uygun değildir . Sorgu planı seçimiyle ilgili sorunlarınız varsa, sorgu performans sorunlarını izlemek için belgelere bakmalısınız . Sadece kuralları belirleyip enable_
uzaklaşmayın.
Dizini kullanmak için çok iyi bir nedeniniz olmadıkça Postgres doğru seçimi yapıyor olabilir. Neden?
Muhtemelen kullanmanın tek geçerli nedeni
set enable_seqscan=false
sorgular yazarken ve tablolarda büyük miktarlarda veri olsaydı sorgu planının gerçekte ne olacağını hızlıca görmek istediğin zamandır. Veya tabii ki sorgunuzun sadece veri kümesi çok küçük olduğu için bir dizin kullanmadığını hızlıca onaylamanız gerekiyorsa.
set enable_seqscan=false
, sorgunuzu çalıştırın ve ardından set enable_seqscan=true
postgresql'yi doğru davranışına döndürmek için hızlıca çalıştırın (ve bunu üretimde yapmayın, yalnızca geliştirme aşamasında!)
SET SESSION enable_seqscan=false
sadece kendini etkilemek için
Bazen PostgreSQL, belirli bir koşul için en iyi dizin seçimini yapamaz. Örnek olarak, herhangi bir gün için birkaç yüz tane olan birkaç milyon satırlık bir işlem tablosu olduğunu ve tablonun dört dizini olduğunu varsayalım: işlem_kimliği, müşteri_kimliği, tarih ve açıklama. Aşağıdaki sorguyu çalıştırmak istiyorsunuz:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL, transaction_date_idx yerine transaction_description_idx dizinini kullanmayı seçebilir, bu da sorgunun bir saniyeden kısa sürmesi yerine birkaç dakika sürmesine neden olabilir. Bu durumda, aşağıdaki gibi koşulu uydurarak dizini tarihte kullanmaya zorlayabilirsiniz:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
, postgresql motorunun bunun yerine yalnızca bir dizi / birincil anahtar taraması gerçekleştirmesi olabilir. Sonuç - PostgreSql sunucusu için bazı indeks kullanımını zorlamak için% 100 güvenilir bir yöntem yoktur.
where
iki tablo dışında bir koşul yoksa veya birleştirilmiş ve Postgres indeksi alamazsa ?
Bu sorun genellikle bir dizin taramasının tahmini maliyeti çok yüksek olduğunda ve gerçeği doğru şekilde yansıtmadığında ortaya çıkar. Bunu random_page_cost
düzeltmek için konfigürasyon parametresini düşürmeniz gerekebilir . Gönderen Postgres belgelerinde :
Bu değerin [...] azaltılması, sistemin indeks taramalarını tercih etmesine neden olacaktır; yükseltmek dizin taramalarının nispeten daha pahalı görünmesini sağlayacaktır.
Daha düşük bir değerin Postgres'in dizini gerçekten kullanıp kullanmayacağını kontrol edebilirsiniz (ancak bunu yalnızca test için kullanın ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
Varsayılan değeri ile SET random_page_cost = DEFAULT;
tekrar geri yükleyebilirsiniz .
Dizin taramaları, sıralı olmayan disk sayfası getirmelerini gerektirir. Postgres, random_page_cost
sıralı getirmelerle ilişkili olarak bu tür sıralı olmayan getirmelerin maliyetini tahmin etmek için kullanır . Varsayılan değer 4.0
, dolayısıyla sıralı getirmelerle karşılaştırıldığında ortalama maliyet faktörünün 4 olduğunu varsayar (önbelleğe alma etkileri hesaba katılarak).
Ancak sorun, bu varsayılan değerin aşağıdaki önemli gerçek hayat senaryolarında uygun olmamasıdır:
1) Katı hal sürücüleri
Belgelerin kabul ettiği gibi:
Sıralı sürücülere göre rasgele okuma maliyeti düşük olan depolama, örneğin katı hal sürücüler için daha düşük bir değerle daha iyi modellenebilir
random_page_cost
.
PostgresConf 2018'deki bir konuşmadan bu slaydın son noktasına göre, katı hal sürücüleri random_page_cost
arasında 1.0
ve 2.0
için bir şeye ayarlanmalıdır .
2) Önbelleğe alınan veriler
Gerekli dizin verileri RAM'de zaten önbelleğe alınmışsa, bir dizin taraması her zaman sıralı bir taramadan önemli ölçüde daha hızlı olacaktır. Belgeler şunu söylüyor:
Buna bağlı olarak, verilerinizin tamamen önbellekte olma ihtimali varsa, [...] düşürmek
random_page_cost
uygun olabilir.
Sorun şu ki, ilgili verilerin önceden önbelleğe alınıp alınmadığını kolayca bilemezsiniz. Bununla birlikte, belirli bir dizin sık sık sorgulanırsa ve sistemde yeterli RAM varsa, veriler büyük olasılıkla önbelleğe alınır ve random_page_cost
daha düşük bir değere ayarlanmalıdır. Farklı değerleri denemeniz ve sizin için neyin işe yaradığını görmeniz gerekecek.
Ayrıca açık veri önbelleği için pg_prewarm uzantısını kullanmak isteyebilirsiniz .
Sorunun kendisi çok geçersiz. Zorlamak (örneğin enable_seqscan = off yaparak) çok kötü bir fikirdir. Daha hızlı olup olmayacağını kontrol etmek faydalı olabilir, ancak üretim kodu asla bu tür hileler kullanmamalıdır.
Bunun yerine - sorgunuzun analizini açıklayın, okuyun ve PostgreSQL'in neden kötü (size göre) planı seçtiğini öğrenin.
Web'de, çıktıları analiz etmeyi açıklamayı okumaya yardımcı olan araçlar var - bunlardan biri de açıkla.depesz.com .
Diğer bir seçenek de, freenode irc ağındaki #postgresql kanalına katılmak ve size yardımcı olmak için oradaki adamlarla konuşmaktır - çünkü sorguyu optimize etmek "bir soru sorun, cevabı alın mutlu olun" meselesi değildir. daha çok bir sohbete benziyor, kontrol edilecek birçok şey, öğrenilecek birçok şey var.
Bir seqscan bir ekleme tercih postgres itmek için bir hile yoktur OFFSET 0
alt sorguda
Bu, ihtiyacınız olan tek şey yalnızca n ilk / son öğe olduğunda, büyük / büyük tabloları birbirine bağlayan istekleri optimize etmek için kullanışlıdır.
Diyelim ki 100 bin (veya daha fazla) girişe sahip birden çok tablo içeren ilk / son 20 öğeyi arıyorsunuz, aradığınız şey ilk 100 veya 1000'de olduğunda tüm sorguyu tüm veriler üzerinde oluşturmanın / bağlamanın bir anlamı yok. girdileri. Örneğin bu senaryoda, sıralı bir tarama yapmanın 10 kattan daha hızlı olduğu ortaya çıkıyor.
bkz nasıl bir alt sorgu inlining gelen Postgres önleyebilir?