Bir sistemin "yavaşladık" şikayetiyle bize gelme hikayesi hep benzerdir: veri büyümüştür, eskiden milisaniyede dönen sorgular saniyeler almaya başlamıştır ve ilk teklif hep aynıdır: "sunucuyu büyütelim." Oysa 16 yıllık tecrübemizde vakaların büyük çoğunluğunda çözüm donanım değil, doğru kurgulanmış birkaç indekstir. Bu yazıda yavaş sorguyu nasıl teşhis ettiğimizi, indeks tasarımının saha kurallarını ve indeksin çare olmadığı durumları gerçek örneklerle anlatıyoruz.
Önce suçluyu bulun: slow query log ve EXPLAIN
Tahminle indeks atmak, karanlıkta dart atmaktır. İlk adım slow query log'u açmak (long_query_time'ı 1 saniyeye, hatta 0.5'e çekin) ve bir hafta veri toplamak. Sonra en sık ve en yavaş sorguları EXPLAIN ile incelemek. Baktığımız üç şey: type sütununda ALL görüyorsak tam tablo taraması var demektir; rows sütunu taranan satır sayısını, Extra sütunundaki "Using filesort" ve "Using temporary" ibareleri pahalı ara işlemleri gösterir.
İndeks tasarımının saha kuralları
- Bileşik indekste sıra her şeydir: WHERE durum = ? AND tarih > ? sorgusu için (durum, tarih) indeksi çalışır; (tarih, durum) aynı performansı vermez. Eşitlik kolonları önce, aralık kolonları sonra.
- En soldan prefix kuralı: (a, b, c) indeksi a ve a+b sorgularına hizmet eder; tek başına b'ye etmez.
- Covering index altın değerinde: sorgunun ihtiyaç duyduğu tüm kolonlar indeksin içindeyse MySQL tabloya hiç gitmez; Extra'da "Using index" görmek budur.
- Fonksiyona sokulan kolon indeks kullanamaz: WHERE DATE(olusturma) = '2026-08-18' yerine aralık sorgusu yazın; ya da MySQL 8'in fonksiyonel indekslerini kullanın.
- Her indeksin yazma maliyeti vardır: INSERT/UPDATE başına her indeks güncellenir. Kullanılmayan indeksleri tespit edip (sys.schema_unused_indexes) kaldırmak da optimizasyondur.
İndeksin kurtaramayacağı durumlar
Dürüst olalım: her derdin ilacı indeks değil. OFFSET 500000 ile sayfalama yapıyorsanız sorun sorgu deseninde; keyset pagination'a geçmek gerekir. N+1 sorgu üreten bir ORM kullanımı, tek tek hızlı ama toplamda ölümcül binlerce sorgu atar; çözüm eager loading'dir. Raporlama sorguları canlı tabloyu kilitliyorsa özet tablolar veya replika üzerinden okuma gündeme gelir. Teşhis doğru konmadan yazılan her çözüm, yeni bir teknik borçtur.
İndeksler canlıdır: bakım rutini
İndeks çalışması tek seferlik bir proje değil, periyodik bir bakım kalemidir. Veri dağılımı değiştikçe dün mükemmel çalışan plan bugün sapıtabilir; optimizer istatistikleri eskiyince ANALYZE TABLE ile tazelemek gerekir. Bizim müşterilerimizde kurduğumuz rutin şöyle: slow query log sürekli açık ve haftalık özet raporu otomatik gelir; çeyrekte bir kullanılmayan indeks taraması yapılır; şemaya eklenen her yeni sorgu deseni için PR aşamasında EXPLAIN çıktısı istenir. Böylece "veritabanı yavaşladı" krizi hiç doğmaz, çünkü yavaşlama daha trend halindeyken yakalanır. Bir de sürüm notu: MySQL 8'in gizli (invisible) indeks özelliği, bir indeksi silmeden önce "görünmez yapıp" etkisini güvenle ölçme imkânı verir; üretimde indeks silmenin en az riskli yolu budur.
Somut bir örnek
Geçen yıl bir araç takip projesinde konum sorgusu 12 saniye sürüyordu; tablo 200 milyon satırdı. (arac_id, zaman) bileşik indeksi ve sorgunun tarih aralığına çekilmesiyle süre 40 milisaniyeye indi. Donanım aynı kaldı. Veritabanınızda benzer şüpheliler varsa bize ulaşın; Rich Design olarak sorgu analizi, indeks tasarımı ve veritabanı sağlık taramasını üretimi durdurmadan yapıyoruz.