SQL İndeksleme ve EXPLAIN
30 saniyede özet
İndeks kitabın arkasındaki dizin gibidir: sorguyu sihirle hızlandırmaz, daha az sayfa okutur. Veritabanı indeksi kullanmıyorsa üç ayrı sebep olabilir ve bunların ikisi indeksin kendisiyle ilgili değildir.
Sorgun yavaş ve veritabanı tabloyu baştan sona okuyor. İlk akla gelen “indeks yok” olur, ama iki ihtimal daha var ve çözümleri farklı.
-
Bayt: Yavaş sorguya indeks ekledim. Artık uçacak!
-
Sen: Uçtu mu?
-
Bayt: Hayır. Veritabanı indeksi hiç kullanmıyor, yine bütün tabloyu okuyor.
-
Bayt: Dizin orada duruyor ama okuyucu ona bakmıyor. Neden?
Adım adım oku
- İndeks yoksa veritabanı istenen satırı bulmak için bütün sayfaları okur.
- B-Tree dizini alfabetik bir kitap dizini gibidir: birkaç adımda doğru sayfaya iner.
- Birden çok sütunlu bir indekste sorgu ilk sütunu kullanmıyorsa dizinde başlanacak bir yer yoktur.
- Koşul satırların büyük kısmını getiriyorsa her sayfa zaten okunacaktır; tabloyu taramak daha ucuzdur.
İndeks ne yapar, ne yapmaz?
Bir indeks sorguyu hızlandırmaz. Okunması gereken sayfa sayısını azaltır.
Milyon satırlık bir tablo 10.000 sayfaya yayılmışsa, indekssiz her sorgu 10.000 sayfa okur. B-Tree ise sıralı bir yapıdır: her seviye aralığı yüzlerce kat daraltır, bu yüzden hedefe 3-4 sayfa okuyarak ulaşılır.
kök [A .......................... Z] 1 sayfaiç düğüm [H ........ M] 1 sayfayaprak [Izmir] → satırın yeri 1 sayfaTablo bir milyon satırdan on milyona çıktı. İndeksle tek bir satırı bulmak şimdi kaç kat daha fazla sayfa okutur? Cevabı göster
Neredeyse hiç fazla okutmaz. Ağacın yüksekliği satır sayısıyla logaritmik büyür; her seviye aralığı yüzlerce kat daralttığı için on kat büyüme çoğu zaman derinliğe bir seviye bile eklemez.
Kafam karıştı, daha basit anlat
İndeks bir kitabın arkasındaki dizin gibidir. Aradığın kelimeyi bütün kitabı okumadan bulursun, çünkü dizin sana hangi sayfaya bakacağını söyler.
Kendin gör
B-Tree indeks — planlayıcı neden indeksi kullanmıyor?
Tohum 7SELECT id, total, created_at FROM orders WHERE customer_id = 4711;
CREATE INDEX ON orders (customer_id); 100.000 satır · 1.000 heap sayfası
- kök — tüm aralık
- iç düğüm — aralık daraldı
- yaprak — customer_id = 4711
İncelenen satır
0
Dönen satır
0
Planlayıcı maliyeti
91/ seq 1.000
Şu an ne oldu?
Ağaçta iniliyor — seviye 0/3
Her seviye aralığı 200 kat daraltıyor. 1000 sayfalık bir tabloda hedefe ulaşmak yalnızca 3 sayfa okumaya bakıyor.
Aklında kalsın: B-Tree yüksekliği satır sayısıyla logaritmik büyür. Tabloyu on kat büyütmek derinliği genelde hiç değiştirmez.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
Sırayla dene — her adım farklı bir dersi gösteriyor:
(customer_id)+customer_id = 4711. İndeks seçiliyor. 1000 sayfa yerine ~23 sayfa.- Tablo boyutunu 500 bine çıkar. Sayfa sayısı neredeyse hiç değişmiyor — ağacın derinliği aynı kaldı. İndeksin asıl gücü bu.
- İndeksi
(status, customer_id)yap, koşulucustomer_idbırak.Seq Scan. İndeks duruyor ama kullanılamıyor. - Koşulu
status = 'PENDING'yap. YineSeq Scan— ama bu sefer sebep farklı: indeks kullanılabilir, sadece çok satır eşliyor. - “Yalnızca indeksteki sütunlar” anahtarını aç. Aynı sorgu artık
Index Only Scan.
Bir B-tree indeks hangi sorguyu hızlandırmaz?
İndeks neden kullanılmaz? Üç sebep
| Sebep | EXPLAIN ne der | Çözüm |
|---|---|---|
| İndeks yok | Seq Scan | İndeks ekle |
| Leftmost prefix kırık | Seq Scan | Sütun sırasını değiştir veya ikinci indeks |
| Koşul çok satır eşliyor | Seq Scan | Çözüm indeks değil — sorguyu veya modeli değiştir |
Kafam karıştı, daha basit anlat
Dizin olsa bile veritabanı onu kullanmayabilir. Çok satır gelecekse kitabı baştan okumak, dizinle tek tek sayfa atlamaktan daha hızlıdır.
`WHERE UPPER(email) = 'A@B.COM'` sorgusu `email` üzerindeki indeksi kullanır mı?
Dizin alfabetiktir: ilk sütun kuralı
Bileşik bir indeks, sütunları soldan sağa sıralar. Önce status’a göre sıralı, her status içinde customer_id’ye göre.
CREATE INDEX ON orders (status, customer_id);
WHERE status = 'PENDING' AND customer_id = 4711 -- ✅ seek edilirWHERE status = 'PENDING' -- ✅ ilk sütun varWHERE customer_id = 4711 -- ❌ ağaçta inilecek yol yokSoyada göre sıralı bir telefon rehberinde “adı Ahmet olanları” bulmak için rehberi baştan sona okumak gerekir.
Sütun sırasını nasıl seçersin.
- Eşitlikle sorgulananlar önce, aralıkla sorgulananlar sonra. Bir aralık koşulundan (
>,BETWEEN,LIKE 'x%') sonraki sütunlar artık seek için kullanılamaz. - En seçici sütun öne.
customer_idbinlerce farklı değer alır,statusbeş. Önecustomer_idgelir. - Sorgularını say, sütunlarını değil. İndeks tasarımı tablo şemasından değil, gerçekten çalışan sorgulardan çıkar.
seçicilikBir koşulun satırların ne kadarını elediği. Sadece birkaç farklı değer alan bir sütunda indeks çoğu zaman hiç kullanılmaz.Tablonun beşte birini okumak, indeksten gidip her satır için tabloya dönmekten ucuzdur — planlayıcı bu hesabı yapar ve indeksi reddeder.Sözlükte gör → — indeksin işe yaramadığı yer. Simülatörde 4. adım tam olarak bunu gösteriyor: status = 'PENDING' koşulu satırların beşte birini eşliyor.
Her sayfada zaten birkaç eşleşme var; indeksten gitmek tüm sayfaları okumak + bir de indeksi okumak demek. Planlayıcı haklı olarak tam taramayı seçiyor.
Az sayıda farklı değer alan bir sütunu (durum, aktif/pasif) tek başına indekslemek işe yaramaz. Seçici bir sütunun arkasına eklenirse işe yarar: (customer_id, status).
Kafam karıştı, daha basit anlat
Telefon rehberi önce soyada, sonra ada göre sıralıdır. Yalnızca adı biliyorsan rehber sana yardım edemez. Bileşik indeks de ilk sütundan başlanarak kullanılır.
`CREATE INDEX ON orders (customer_id, status, created_at)` varken her sorguyu indeksin kullanılıp kullanılmadığına göre ayır.
covering indexSorgunun ihtiyaç duyduğu tüm sütunları içeren indeks. Veritabanı tabloya hiç gitmeden cevabı indeksten okur — index-only scan.Sözlükte gör →. Simülatörde 5. adımda ne değişti: sorgunun istediği tüm sütunlar indekste olduğu için heap’e hiç gidilmedi.
-- indeks customer_id ve status tutuyor, sorgu başka sütun istemiyorSELECT customer_id, status FROM orders WHERE status = 'PENDING';--> Index Only ScanNormal bir Index Scan iki adımdır: indeks satırın yerini söyler, sonra satır heap’ten okunur. Covering index ikinci adımı kaldırır; bedeli daha büyük bir indeks ve daha yavaş yazmalardır.
Aşağıdaki indeksler bir bankanın hesap hareketleri ekranından ve bekleyen EFT kuyruğundan çıkarıldı.
Derinleş · Hesap hareketleri: sorgudan çıkan indeksler 4 dosya · ~61 satır · ilk okumada atlayabilirsin
İndeks eklemenin bedeli nedir?
Tuzaklar ve EXPLAIN okumak
Index Scan using idx_orders_customer on orders (cost=0.42..8.45 rows=20 width=48) (actual time=0.03..0.09 rows=20 loops=1)rows=20(tahmin) ilerows=20(gerçek) arasındaki fark. Büyük sapma, istatistiklerin bayatladığını söyler —ANALYZEçalıştırmak gerekir. Kötü plan seçimlerinin en yaygın sebebi budur.loops=1. Büyük bir sayı, iç içe döngüde tekrar tekrar çalışan bir plan demektir.- Plan adı değil, satır sayısı.
Seq Scangörmek kötü değildir; 20 satır dönerkenSeq Scangörmek kötüdür.
EXPLAIN sorguyu çalıştırmaz, yalnızca planı gösterir. Gerçek süreleri görmek için EXPLAIN ANALYZE gerekir — ve bu sorguyu gerçekten çalıştırır.
Kendini sına
İndeks, okunması gereken sayfa sayısını azaltarak işe yarar.
Bu indeks varken, aşağıdaki sorgunun planı ne olur?
Aklında kalacak üç şey
- 1 İndeks sorguyu hızlandırmaz, okunacak sayfa sayısını azaltır. Az sayfa okunmayacaksa hiçbir şey kazandırmaz.
- 2 Birden çok sütunlu indekste sütun sırası bir tercih değil, şarttır: sorgu ilk sütunu daraltmıyorsa dizinde başlanacak bir yer yoktur.
- 3 Veritabanı indeksi üç sebeple kullanmayabilir: indeks yok, ilk sütun kullanılmıyor ya da koşul çok fazla satır getiriyor. Üçünün çözümü farklıdır.
4 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.
- System Design & Dağıtık SistemlerURL Kısaltıcı Tasarımı — Parçaları Tek Sistemde BirleştirmekKısa kodu adresin hash'inden kesip alıyorsun. Milyarlarca linkte bir gün ne olur?Derse git
- VeritabanlarıSorgu Planı ve Join Algoritmaları — Planner Neden Bu Yolu Seçti?Aynı sorgu dün bir saniyede, bugün bir dakikada bitti. Veritabanı neyi yanlış tahmin etti?Derse git
- VeritabanlarıSayfalama — OFFSET mi, Keyset mi?İlk sayfa bir anda geliyor, 500. sayfa saniyeler sürüyor. Aynı 20 satır değil mi?Derse git
- VeritabanlarıTam Metin Arama — LIKE '%kira%' Neden Hem Yavaş Hem Eksik?Müşteri hareketlerinde "kira" aradı ve altı ödemesinden yalnızca dördünü buldu. Üstelik sorgu yavaştı. İkisi aynı sebepten mi?Derse git