Sorgu Planı ve Join Algoritmaları — Planner Neden Bu Yolu Seçti?
Önce şunu oku: SQL İndeksleme ve EXPLAIN
30 saniyede özet
Veritabanı iki tabloyu üç farklı yolla birleştirebilir ve hangisini seçeceğine 'kaç satır gelir?' tahminiyle karar verir. Tahmin yanlışsa beş kişilik plan beş yüz kişiye uygulanır. Planı okurken önce tahmini gerçekle karşılaştır.
Gece toplu yükleme yapıldı ve sabah raporu saatlerce bitmedi. Dün aynı sorgu iki saniyede bitiyordu. Kod değişmemişti.
-
Bayt: Sabah raporu saatlerce bitmedi! Dün aynı sorgu iki saniyede bitiyordu.
-
Sen: Kodda ne değişti?
-
Bayt: Hiçbir şey! Sadece gece büyük bir yükleme yapıldı.
-
Bayt: Beş misafir bekleyip beş yüz ağırlarsan alışverişi nasıl yaparsın?
Birleştirmenin üç yolu
| Algoritma | Nasıl çalışır | İyi olduğu yer |
|---|---|---|
| Nested loop | Dıştaki her satır için içeride arar | Dış taraf küçük, iç tarafta index var |
| Hash join | Küçük tarafı bellekte hash tablosuna koyar, büyük tarafı bir kez tarar | İki taraf da büyük, eşitlik koşulu |
| Merge join | İki sıralı girdiyi fermuar gibi birleştirir | Girdiler zaten sıralı (ör. index sırası) |
Hangisinin iyi olduğu satır sayısına bağlıdır. Beş sipariş için index’te beş arama, milyon satırı taramaktan ucuzdur; yüz bin sipariş için tersi.
Kafam karıştı, daha basit anlat
Beş kişiyi bulmak için telefon rehberinde tek tek aramak hızlıdır. Yüz bin kişiyi bulacaksan rehberi baştan sona okumak daha hızlı olabilir.
Bir müşterinin 5 siparişini, 10 milyon satırlık order_items tablosuyla birleştiriyorsun; order_items(order_id) üzerinde index var. Hangi join algoritması büyük ihtimalle en ucuzu?
Karar bir tahmine dayanır
query plannerBir SQL sorgusu için olası yürütme planlarının maliyetini tahmin edip en ucuz görüneni seçen bileşen. Maliyet tabloların istatistiklerine dayanır.Sözlükte gör →, her olası planın maliyetini hesaplar ve en ucuzunu seçer. Maliyetin temel girdisi, her adımda kaç satır geleceğine dair satır tahminiPlanner'ın bir plan adımından kaç satır çıkacağına dair tahmini. İstatistikler eskiyse tahmin yanlış olur ve maliyet hesabı onunla birlikte yanılır.Sözlükte gör →.
Tablo dün boştu ve istatistikleri o zaman toplandı. Gece yüz bin sipariş yüklendi. Planner bugünün siparişleri için kaç satır bekler? Cevabı göster
Neredeyse hiç. İstatistikler güncellenmediyse planner tabloyu hâlâ boş sanır ve bir satır için en ucuz planı seçer. Index yoksa bu, yüz bin sipariş için milyon satırlık tabloyu yüz bin kez taramak demektir.
Adım adım oku
- İstatistikler tablo boşken toplanmış: planner beş satır bekliyor ve tezgâh tezgâh dolaşan nested loop'u seçiyor.
- Gece yüz bin sipariş yüklendi ama planner'ın defteri güncellenmedi.
- Nested loop çok sayıda satırda tek tek dolaşmaya devam eder; rapor saatler sürer.
- ANALYZE ile istatistikler yenilenince planner gerçek sayıyı görür ve toptancıya gider gibi hash join'i seçer.
Autovacuum ya da otomatik istatistik güncellemesi eninde sonunda devreye girer. Ama toplu yüklemeden hemen sonra çalışan bir sorgu onu beklemez.
Kafam karıştı, daha basit anlat
Veritabanı hangi yolun ucuz olduğuna tahminle karar verir. Elindeki bilgi eskiyse, yanlış yolu seçer. Bu yüzden istatistikler güncel tutulmalı.
Gece toplu yükleme sonrası sabah raporu, dün iki saniye sürerken bugün saatlerce sürüyor. Kod ve index'ler aynı. En olası sebep?
Kendin gör
Planner tahminle seçer, sorgu gerçekle çalışır
Tohum 625264Oynat ya da adımla.
Şu an ne oldu?
orders ⋈ order_items (1.000.000 satır)
Planner, süzgeçten kaç sipariş çıkacağını tahmin edip en ucuz görünen join algoritmasını seçecek.
Görevler0/3
Planner'a en iyisinden en az 10 kat pahalı bir plan seçtiraçık
İpucu
Tahmin ile gerçek arasındaki farkı büyüt.
Nested loop'un en doğru seçim olduğu durumu bulaçık
İpucu
Az satır ve arama yapılabilecek bir yol.
Çok satırda planner doğru biçimde hash join seçsinaçık
İpucu
İstatistikler gerçeği bilsin.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Eski istatistik, index yok: planner nested loop seçti, gerçek iş on binlerce kat fazla.
- İstatistikleri güncelle. Planner hash join’e geçti.
- Eski istatistiğe dön, index’i aç. Yine yanlış plan, ama bu kez hasar küçük.
- Sipariş sayısını “az” yap. Nested loop gerçekten en iyi seçim.
Simülatörde istatistikler eskiyken index açılınca planner yine yanlış plan seçti ama hasar küçük kaldı. Neden?
Plan nasıl okunur?
Üç motorun da planı gerçek satır sayılarıyla gösteren bir yolu var:
-- PostgreSQLEXPLAIN (ANALYZE, BUFFERS) SELECT …;
-- SQL Server (SSMS'te "Include Actual Execution Plan" ya da)SET STATISTICS XML ON;
-- OracleSELECT /*+ gather_plan_statistics */ …;SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR(format => 'ALLSTATS LAST'));İlk bakacağın yer, her düğümde tahmini satır ile gerçek satır arasındaki farktır. Fark büyükse sorun çoğu zaman index değil, istatistiktir.
Bunu bir bankanın sabah mutabakatında izleyelim. Gece kart işlemleri toplu yükleniyor, sabah da her işlemin kart kuruluşunun takas dosyasında karşılığı var mı diye bakılıyor.
Derinleş · Kart takas mutabakatı: yükle, ANALYZE et, birleştir 4 dosya · ~62 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Planı okurken iki sayıyı yan yana koy: veritabanının tahmin ettiği satır sayısı ve gerçekten gelen. Arada büyük fark varsa sorun oradadır.
PostgreSQL'de `EXPLAIN ANALYZE` çıktısında bir düğüm `rows=1` tahmin edip `actual rows=184000` gösteriyor ve üstünde bir Nested Loop var. İlk ne yaparsın?
Hash join için ayrılan bellek, küçük tarafın hash tablosunu almaya yetmezse ne olur?
Tuzaklar
Toplu yüklemeden sonra istatistik yok. Yüklemenin sonuna ANALYZE (PostgreSQL), UPDATE STATISTICS (SQL Server) ya da DBMS_STATS.GATHER_TABLE_STATS (Oracle) ekle.
parameter sniffingSQL Server'ın bir sorgunun planını ilk çağrıdaki parametre değerine göre derleyip önbellekte tutması. Plan o değere uygunsa diğerlerinde kötü çalışabilir.Sözlükte gör →. SQL Server planı ilk gelen parametreye göre derleyip önbelleğe alır; nadir bir müşteri için seçilen plan, en büyük müşteride çöker.
Kolona fonksiyon. WHERE lower(email) = … düz bir index’i kullanamaz; ifade üzerinde index gerekir.
Tip uyuşmazlığı. SQL Server’da varchar kolonu nvarchar parametreyle karşılaştırmak dönüşüme ve taramaya yol açabilir.
SQL Server'da bir saklı yordam çoğu müşteri için hızlı, en büyük müşteri için çok yavaş. Yordamı yeniden derleyince bir süre düzeliyor. Olası sebep ve tipik çözümler?
users(email) üzerinde index var ama giriş sorgusu tüm tabloyu tarıyor. Hatalı satır hangisi?
Kendini sına
Veritabanı birleştirme yöntemini, kaç satır geleceği tahminine göre seçer.
PostgreSQL'de JDBC üzerinden çalışan bir sorgu ilk birkaç çağrıda hızlı, sonra bir anda yavaşlıyor; psql'den elle çalıştırınca hızlı. Olası açıklama?
Aklında kalacak üç şey
- 1 Nested loop az satırda ve index varken, hash join çok satırda, merge join sıralı girdilerde iyidir. Hiçbiri her zaman en iyisi değildir.
- 2 Veritabanı maliyeti satır tahminiyle hesaplar. İstatistikler eskiyse tahmin yanlış olur ve plan gerçek veri için yanlış seçilir.
- 3 Bir planı okurken ilk bakılacak yer, tahmini satır sayısı ile gerçek satır sayısı arasındaki farktır.
5 kart sonraki derste seni bekliyor