Veritabanı Index Mantığı: Yavaş Sorgular Nasıl Hızlanır?
09.07.2026 · 3 dk okuma · 1 okunma
Veritabanı index yapısı, bir kitabın arkasındaki dizine benzer. Dizin olmadan bir kelimeyi bulmak için tüm sayfaları çevirmek gerekir; dizinle birlikte doğrudan ilgili sayfaya gidilir. Uygulamalardaki yavaşlığın büyük bölümü, kodun karmaşıklığından değil, bu dizinin eksikliğinden kaynaklanır.

Index ne yapar, ne yapmaz?
Index, veritabanının bir sütundaki değerleri sıralı bir yapıda (genelde B-ağacı) tutmasıdır. Böylece "müşteri numarası 4213 olan kayıt" aranırken milyonlarca satır taranmaz, birkaç adımda ulaşılır.
Karşılığında iki bedel vardır: index disk alanı kaplar ve her INSERT, UPDATE, DELETE işleminde güncellenmesi gerekir. Bu yüzden "her ihtimale karşı index ekleyelim" yaklaşımı, okuma hızını artırırken yazma performansını sessizce düşürür.
Hangi sütunlara index gerekir?
- WHERE koşulunda kullanılanlar: Filtrelemenin yapıldığı sütunlar ilk adaydır.
- JOIN edilen sütunlar: Yabancı anahtarlar neredeyse her zaman indexlenmelidir.
- ORDER BY ve GROUP BY sütunları: Sıralama, index sayesinde ek işlem gerektirmeden yapılabilir.
- Benzersizlik gereken alanlar: E-posta, kullanıcı adı gibi alanlarda benzersiz index hem hız hem veri bütünlüğü sağlar.
Buna karşılık, iki-üç farklı değer alan sütunlarda (örneğin "aktif/pasif") tek başına index çoğu zaman işe yaramaz; veritabanı zaten tablonun yarısını okuyacaktır.
Bileşik index ve sıra meselesi
Birden çok sütunu kapsayan index, soldan sağa doğru çalışır. (musteri_id, siparis_tarihi) şeklindeki bir index şu sorgularda kullanılır:
WHERE musteri_id = 5— kullanılırWHERE musteri_id = 5 AND siparis_tarihi > '2026-01-01'— tam verimle kullanılırWHERE siparis_tarihi > '2026-01-01'— kullanılmaz
Bu yüzden bileşik index tasarlarken, sorgularınızda hangi sütunun her zaman bulunduğunu belirleyip onu başa koymak gerekir.
Index’i etkisiz kılan yaygın hatalar
Doğru index olsa bile bazı yazım biçimleri onu devre dışı bırakır:
- Sütunu fonksiyona sokmak:
WHERE YEAR(tarih) = 2026yerineWHERE tarih >= '2026-01-01' AND tarih < '2027-01-01'yazın. - Baştan joker karakter:
LIKE '%metin'index kullanamaz; tam metin araması gerekiyorsa farklı bir index türü düşünülmelidir. - Tip uyumsuzluğu: Sayısal bir sütunu metin olarak karşılaştırmak dönüşüm yaratır ve index devre dışı kalır.
- Gereksiz OR: Farklı sütunlar üzerinde OR kullanımı, planlayıcıyı tam taramaya itebilir; UNION ile bölmek bazen daha hızlıdır.
EXPLAIN çıktısını okumak
Tahmin yerine ölçüm için sorgunun başına EXPLAIN yazın. Bakılacak üç sütun vardır:
- type:
ALLise tam tablo taraması yapılıyor demektir;ref,range,constdaha iyidir. - key: Hangi index kullanıldı? Boşsa index yok veya kullanılamıyor.
- rows: Kaç satır taranacağı tahmini. Sonuç 20 satırken burada 500.000 yazıyorsa sorun vardır.
MySQL tarafında ayrıntılı açıklamalar için resmî EXPLAIN belgeleri incelenebilir.
Index’ten önce bakılması gereken: N+1 problemi
Uygulamalardaki yavaşlığın en sık nedeni index eksikliği değil, döngü içinde sorgu çalıştırmaktır. Sipariş listesi çekilip her sipariş için müşteri adı ayrı sorguyla alınırsa, 100 satırlık sayfa 101 sorgu üretir. Her sorgu 2 milisaniye sürse bile toplam süre kabul edilemez hâle gelir.
Çözüm iki yoldan biridir: ilişkili tabloyu tek sorguda JOIN etmek ya da ilişkili kayıtları tek seferde toplu çekip bellekte eşleştirmek. Bu düzeltme çoğu zaman index eklemekten daha büyük kazanç sağlar.
Kapsayıcı index ve sık kullanılan bir hile
Bir sorgunun ihtiyaç duyduğu tüm sütunlar index içinde bulunuyorsa, veritabanı asıl tabloya hiç gitmez. Buna kapsayıcı (covering) index denir ve okuma yükünü belirgin biçimde azaltır.
Örnek: sık çalışan bir sorgu yalnızca müşteri numarası, sipariş tarihi ve tutar bilgisini okuyorsa, bu üç sütunu içeren bir index sayesinde sorgu tamamen index üzerinden cevaplanır. Ancak bu tekniği her sorgu için uygulamak yanlıştır; index genişledikçe yazma maliyeti ve disk kullanımı artar. Kural şudur: yalnızca çok sık çalışan ve performansı kritik olan sorgular için kapsayıcı index düşünülmelidir.
Benzer şekilde, yalnızca belirli koşuldaki satırları kapsayan kısmi indexler de bazı veritabanlarında desteklenir; örneğin yalnızca "aktif" kayıtları indexlemek, index boyutunu ciddi biçimde küçültür.
Bakım: index’ler de yaşlanır
Zamanla kullanılmayan indexler birikir. Düzenli olarak hangi indexlerin kullanıldığını inceleyin ve kullanılmayanları kaldırın. Ayrıca tablo büyüdükçe istatistikler eskir; veritabanının planlayıcısı yanlış karar vermeye başlarsa istatistiklerin güncellenmesi gerekir.
Sonuç
Index kurmak sihir değil, ölçüme dayalı bir mühendislik işidir. Önce yavaş sorguları tespit edin, EXPLAIN ile nedenini görün, gerekiyorsa doğru sırayla bileşik index kurun ve sonucu tekrar ölçün. Bu döngüyü uyguladığınızda, çoğu "sunucu yetersiz" şikâyetinin aslında birkaç eksik index olduğunu göreceksiniz.
Sık Sorulan Sorular
Olmaz. Her index disk alanı tüketir ve her ekleme, güncelleme, silme işleminde ayrıca güncellenir. Gereksiz indexler yazma performansını düşürür ve sorgu planlayıcısını yanıltabilir.
Bileşik index soldan sağa çalışır. (musteri_id, tarih) indexi yalnızca musteri_id ile yapılan sorguda da kullanılır, ancak yalnızca tarih ile yapılan sorguda kullanılamaz. Sıra, hangi sorguların hızlanacağını belirler.
Sorgunun başına EXPLAIN yazın. Çıktıda type sütununun ALL olması tam tablo taraması, ref veya range olması index kullanıldığı anlamına gelir. rows sütunu taranan tahmini satır sayısını gösterir.
Bir liste çekip ardından her satır için ayrı sorgu çalıştırmaktır. 50 kayıtlık listede 51 sorgu demektir. Çözüm, ilişkili verileri tek sorguda birleştirmek veya toplu olarak önceden yüklemektir.