Bir site yavaşladığında ilk tahmin genellikle sunucunun yetersiz olduğu yönünde olur. Benim tecrübemde ise vakaların büyük kısmında sorun, birkaç kötü yazılmış ya da indekssiz sorgu. Daha büyük bir sunucuya geçmeden önce bu sorguları bulmak hem daha ucuz hem daha kalıcı bir çözüm.
1. Yavaş sorgu kaydını açın
MySQL, belirli bir süreden uzun süren sorguları bir dosyaya yazabilir. Sunucuyu yeniden başlatmadan, çalışırken açmak mümkün:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5; -- 0,5 saniyeden uzun sürenler
SET GLOBAL log_queries_not_using_indexes = 'ON';
SHOW VARIABLES LIKE 'slow_query_log_file'; -- kaydın yazıldığı dosya
Bu ayarlar MySQL yeniden başladığında sıfırlanır. Kalıcı olması için my.cnf dosyasının [mysqld] bölümüne ekleyin:
slow_query_log = 1
long_query_time = 0.5
slow_query_log_file = /var/lib/mysql/slow.log
log_queries_not_using_indexes ayarı küçük tablolarda da çok sayıda kayıt üretebilir. Birkaç gün inceleme yaptıktan sonra kapatmanızı öneririm.
2. En pahalı sorguları özetleyin
Log dosyası kısa sürede binlerce satıra ulaşır. MySQL ile birlikte gelen mysqldumpslow aracı, aynı kalıptaki sorguları gruplayıp toplam süreye göre sıralar:
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log
-s t toplam süreye göre sıralar, -t 10 ilk on kalıbı gösterir. Burada önemli olan, en yavaş tek sorgu değil, toplamda en çok süre harcatan sorgu. Tek seferde 2 saniye süren bir rapor sorgusu, her sayfa açılışında 80 milisaniye harcayan bir sorgudan çok daha az sorun yaratır.
3. EXPLAIN ile sorgunun planına bakın
Sorunlu sorguyu bulduktan sonra başına EXPLAIN ekleyerek MySQL'in bu sorguyu nasıl çalıştırmayı planladığını görebilirsiniz. Örnek olarak bir blogun kategori sayfasındaki sorgu:
EXPLAIN SELECT id, title, slug, published_at
FROM posts
WHERE category_id = 4 AND is_published = 1
ORDER BY published_at DESC
LIMIT 12;
Çıktıda dikkat edilecek sütunlar:
| Sütun | Ne anlatır? | Kötü işaret |
|---|---|---|
type | Tabloya nasıl erişildiği | ALL: tablonun tamamı taranıyor |
key | Kullanılan indeks | NULL: hiçbir indeks kullanılmıyor |
rows | Okunacağı tahmin edilen satır sayısı | Dönen satır sayısından çok büyük |
Extra | Ek işlemler | Using filesort, Using temporary |
Yukarıdaki sorgu için ilk çıktı type: ALL, key: NULL ve Using filesort gösteriyordu: 12 satır döndürmek için tablonun tamamı okunup sıralanıyor.
4. Doğru indeksi ekleyin
Bileşik (birden fazla sütunlu) indeks kurarken sıralama önemli. Genel kural: önce eşitlikle (=) filtrelenen sütunlar, sonra sıralama ya da aralık (>, BETWEEN) kullanılan sütun.
ALTER TABLE posts
ADD INDEX idx_kategori_yayin (category_id, is_published, published_at);
İndeksten sonra aynı EXPLAIN çıktısında type: ref, key: idx_kategori_yayin ve çok daha küçük bir rows değeri görülür; Using filesort da kaybolur. Çünkü veriler indekste zaten tarih sırasına göre duruyor.
MySQL 8.0.18 ve sonrasında EXPLAIN ANALYZE komutu sorguyu gerçekten çalıştırıp her adımın ne kadar sürdüğünü gösterir. Tahmin ile gerçeğin ne kadar örtüştüğünü görmek için çok faydalı.
Sık karşılaşılan sebepler
- Sütuna fonksiyon uygulamak:
WHERE YEAR(published_at) = 2026indeksi kullanamaz. Bunun yerineWHERE published_at >= '2026-01-01' AND published_at < '2027-01-01'yazın. - Başında joker karakter olan LIKE:
LIKE '%kelime%'indeks kullanamaz. Ciddi arama ihtiyacı içinFULLTEXTindeks ya da ayrı bir arama motoru düşünün. - Gereksiz
SELECT *: Büyük metin sütunlarını listeleme sayfalarında çekmek hem diski hem belleği yorar. - N+1 sorgu: Tek tek çok sayıda küçük sorgu, slow log'a hiç düşmeden siteyi yavaşlatabilir. Laravel gibi ORM kullanan projelerde ilişkileri
with()ile önceden yüklemek gerekiyor. - Karakter seti uyuşmazlığı: Farklı karakter setine sahip iki sütunu birleştirmek (JOIN) indeksin kullanılmasını engelleyebilir. Bu konuyu latin5'ten utf8mb4'e geçiş yazısında ayrıntılı anlattım.
İndeks her zaman iyi mi?
Hayır. Her indeks, INSERT ve UPDATE işlemlerini biraz yavaşlatır ve disk kaplar. Kullanılmayan indeksleri bulmak için MySQL 8'de sys.schema_unused_indexes görünümüne bakabilirsiniz. Amaç her sütuna indeks eklemek değil, en sık çalışan sorgulara uygun birkaç iyi indeks kurmak.