Postgres'i belirli bir dizini kullanmaya nasıl zorlarım?


112

Postgres'i, aksi halde sıralı bir tarama yapmakta ısrar edecekse bir dizini kullanmaya nasıl zorlarım?



1
+1 Bu özelliği görmeyi çok isterim. Diğer yanıtların dediği gibi, bu basitçe sıralı taramayı devre dışı bırakmak değildir: PG'yi belirli bir dizini kullanmaya zorlama yeteneğine ihtiyacımız var . Bunun nedeni, gerçek kelimede istatistiklerin tamamen yanlış olabilmesidir ve bu noktada güvenilmez / kısmi geçici çözümler kullanmanız gerekir. Basit durumlarda önce dizinleri ve diğer ayarları kontrol etmeniz gerektiğini kabul ediyorum, ancak büyük veride güvenilirlik ve gelişmiş kullanımlar için buna ihtiyacımız var.
collimarco

MySQL ve Oracle'ın her ikisinde de var ... Postgres'in planlayıcısının neden bu kadar güvenilmez olduğundan emin değilim.
Kevin Parker

Yanıtlar:


103

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_seqscanve enable_indexscanparametrelerini 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?

  • Küçük tablolar için sıralı taramalar yapmak daha hızlıdır.
  • Postgres, veri türleri düzgün eşleşmediğinde dizinleri kullanmaz, uygun yayınlar eklemeniz gerekebilir.
  • Planlayıcı ayarlarınız sorunlara neden olabilir.

Ayrıca bu eski haber grubu gönderisine bakın .


4
Kabul edildi, Postgres'i kendi yönteminizle yapmaya zorlamak genellikle yanlış yaptığınız anlamına gelir. 9/10 kez planlayıcı bulabileceğiniz her şeyi yener. Diğer 1 kez yanlış yaptığınız için.
Kent Fredric

Endeks tutmanızın gerçekten operatör sınıflarını kontrol etmek için iyi bir fikir olduğunu düşünüyorum.
metdos

2
Eski bir soruyu canlandırmaktan nefret ediyorum, ancak Postgres belgelerinde, tartışmalarda ve burada sık sık görüyorum, ancak küçük bir masa için neyin uygun olduğuna dair genelleştirilmiş bir kavram var mı? 5000 satır veya 50000 vb. Gibi bir şey mi?
waffl

1
@waffl Kıyaslamayı düşündünüz mü? Bir indeksi olan basit bir tablo ve onu n sıra rastgele önemsiz ile doldurmak için eşlik eden bir işlev oluşturun . Sonra farklı n değerleri için sorgu planına bakmaya başlayın . Dizini kullanmaya başladığını gördüğünüzde, bir ballpark cevabınız olmalıdır. Ayrıca PostgreSQL, bir indeks taramasının çok fazla satırı ortadan kaldırmayacağını belirlerse (istatistiklere dayanarak) sıralı taramalar da alabilirsiniz. Dolayısıyla, gerçek performans endişeleriniz olduğunda kıyaslama her zaman iyi bir fikirdir. İkincil, anekdotsal bir tahmin olarak, birkaç binin genellikle "küçük" olduğunu söyleyebilirim.
jpmc26

11
Oracle, Teradata ve MSSQL gibi platformlarda 30 yılı aşkın tecrübemle, PostgreSQL 10'un optimize edicisini özellikle akıllı bulmuyorum. Güncel istatistiklerle bile, özel bir yöne zorlanandan daha az verimli yürütme planları üretir. Bu sorunları telafi etmek için yapısal ipuçları sağlamak, PostgreSQL'in daha fazla pazar segmentinde büyümesini sağlayacak bir çözüm sağlayacaktır. BENİM NACİZANE FİKRİME GÖRE.
Guido Leenders

75

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.


41
bu kısa cevap aslında test amaçlı iyi bir ipucu veriyor
dwery

3
Kimse soruyu cevaplamıyor!
Ivailo Bardarov

@IvailoBardarov Diğer tüm önerilerin burada olmasının nedeni PostgreSQL'in bu özelliğe sahip olmamasıdır; bu, geliştiriciler tarafından tipik olarak nasıl kullanıldığına ve neden olduğu uzun vadeli sorunlara göre verilen bilinçli bir karardı.
jpmc26

Test etmek için güzel bir numara: çalıştırın set enable_seqscan=false, sorgunuzu çalıştırın ve ardından set enable_seqscan=truepostgresql'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!)
Brian Hellekin

2
@BrianHellekin Daha iyi, SET SESSION enable_seqscan=falsesadece kendini etkilemek için
Izkata

20

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

3
İyi fikir. Ancak, bu yöntemle mevcut dizin kullanımını devre dışı bıraktığımızda - postgresql sorgu iyileştirici bir sonraki uygun dizine geri dönüşler. Bu nedenle, optimize edicinin seçeceğinin garantisi yoktur 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.
Agnius Vasiliauskas

Ya whereiki tablo dışında bir koşul yoksa veya birleştirilmiş ve Postgres indeksi alamazsa ?
Luna Lovegood

@Surya yukarıdakiler hem WHERE hem de JOIN ... ON koşulları için geçerlidir
Ziggy Crueltyfree Zeitgeister

18

Kısa cevap

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_costdü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 .

Arka fon

Dizin taramaları, sıralı olmayan disk sayfası getirmelerini gerektirir. Postgres, random_page_costsı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_costarasında 1.0ve 2.0iç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_costuygun 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_costdaha 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 .



2
Ubuntu'da Pg 10.1'de indeks taramasının büyük (~ 600M satır tablosu) üzerinde çalışması için random_page_cost = 0.1 ayarlamam bile gerekiyordu. Tweak olmadan, seq taraması (paralel olmasına rağmen) 12 dakika sürüyordu (Analiz tablosunun gerçekleştirildiğine dikkat edin!). Sürücü SSD'dir. Ayarlamadan sonra yürütme süresi 1 saniye oldu.
Anatoly Alekseev

Günümü kurtardın. Her iki uçta da analiz çalıştırdıktan sonra bile, aynı veritabanındaki aynı sorgunun bir makinede 30 saniye ve diğerinde 1 saniyeden daha az sürdüğünü anlamaya çalışırken çıldırıyordum ... Kimi ilgilendiriyor olabilir: komut ' ALTER SYSTEM SET random_page_cost = x 'yeni varsayılan değeri global olarak belirler.
Julien

10

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.


2

Bir seqscan bir ekleme tercih postgres itmek için bir hile yoktur OFFSET 0alt 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?


İyi numara. İyi bir optimize edicinin elbette 0 :-) ofseti optimize etmesi
Guido Leenders
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.