"Veritabanı yavaş" cümlesi bir teşhis değildir. Yavaşlığın kaynağı neredeyse her zaman birkaç belirli sorgudur ve bunlar ölçülerek bulunur. Sunucuya kaynak eklemek, indekssiz bir tam tablo taramasını yalnızca biraz daha hızlı yapar.
Yavaş sorgu günlüğünü açın
MySQL yapılandırmasına şu satırları ekleyip servisi yeniden başlatın:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
Bir gün çalıştırın, sonra mysqldumpslow veya pt-query-digest
ile özetleyin. Bu araçlar sorguları normalleştirip toplam süreye göre sıralar. Dikkat:
en uzun süren tek sorgu değil, toplam süresi en yüksek sorgu ailesi
önemlidir. 20 ms süren ama saniyede 200 kez çalışan bir sorgu, 2 saniyelik günlük bir
rapordan çok daha pahalıdır.
Günlüğü beklemeden bakmak
Bir gün beklemek istemiyorsanız sys şeması aynı bilgiyi anlık verir:
SELECT query, exec_count, total_latency, rows_examined_avg
FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 10;
rows_examined_avg sütununa özellikle bakın. Taranan satır sayısının dönen
satır sayısına oranı, indeks eksikliğinin en dürüst göstergesidir. On satır döndürmek için
yüz bin satır taranıyorsa aranacak başka bir sebep yoktur.
EXPLAIN çıktısını okuyun
Sorgunun başına EXPLAIN ekleyin. Bakılacak alanlar:
- type —
ALLtam tablo taraması demektir; küçük tablolar dışında kötüdür.ref,rangeveconstiyidir. - key — hangi indeksin kullanıldığı.
NULLise indeks kullanılmıyor. - rows — taranması beklenen satır sayısı. Sonuçta dönen satır sayısıyla arasındaki fark ne kadar büyükse, indeks o kadar yetersizdir.
- Extra —
Using filesortveUsing temporaryifadeleri sıralama ya da gruplama için geçici yapı kurulduğunu gösterir; genellikle doğru bileşik indeksle ortadan kalkar.
type değeri | Anlamı | Değerlendirme |
|---|---|---|
const, eq_ref | Birincil ya da tekil anahtarla tek satır | En iyi |
ref | İndeksle sınırlı sayıda satır | İyi |
range | İndeks üzerinde aralık taraması | Kabul edilebilir |
index | Tüm indeksin baştan sona taranması | Şüpheli, incelenmeli |
ALL | Tam tablo taraması | Küçük tablolar dışında kötü |
Somut bir örnek
200 bin satırlık bir sipariş tablosunda şu sorguyu düşünün:
SELECT id, tutar FROM siparis WHERE musteri_id = 42 ORDER BY tarih DESC LIMIT 10;
İndeks yokken EXPLAIN çıktısı type: ALL verir, rows
alanında tablonun tamamı görünür ve Extra alanında Using filesort
yazar — sıralama ayrıca yapılmak zorundadır. (musteri_id, tarih) bileşik
indeksi eklendiğinde type alanı ref'e döner, taranan satır sayısı
o müşterinin sipariş adedine iner ve filesort kaybolur, çünkü indeks zaten
tarihe göre sıralıdır. Tek bir ALTER TABLE ile sorgu, tablo büyüdükçe yavaşlayan
bir sorgu olmaktan çıkar.
İndeks eklerken üç kural
- Sıra önemlidir. Bileşik indekste sütun sırası soldan sağa çalışır.
(musteri_id, tarih)indeksi, yalnızmusteri_idile yapılan aramada da kullanılır; yalnıztarihile yapılanda kullanılmaz. - Sütunu sarmalamayın.
WHERE YEAR(tarih) = 2026yazarsanız indeks devre dışı kalır. Doğrusu aralık kullanmaktır:WHERE tarih >= '2026-01-01' AND tarih < '2027-01-01'. - Fazla indeks de maliyettir. Her indeks, yazma işlemlerini yavaşlatır
ve disk yer kaplar. Kullanılmayan indeksleri tespit edip kaldırın:
SELECT * FROM sys.schema_unused_indexes;sunucunun son açılışından beri hiç okunmamış indeksleri listeler. Birkaç haftalık kesintisiz çalışmadan sonra bu liste güvenilir hale gelir.
Kapsayan indeks
Sorgunun ihtiyaç duyduğu tüm sütunlar indeksin içindeyse veritabanı tabloya hiç
gitmez. SELECT durum, tutar FROM siparis WHERE musteri_id = 42 sorgusu için
(musteri_id, durum, tutar) indeksi, tablo satırlarını okumadan cevap verir.
Sık çalışan raporlarda bu yöntem birkaç kat hızlanma sağlar.
Sık görülen dört desen
SELECT *— ihtiyaç duyulmayan sütunları da taşır, kapsayan indeks olasılığını yok eder.- N+1 sorgusu — listedeki her satır için ayrı sorgu atılması. Uygulama tarafında tek sorguya toplanır; ORM kullanıyorsanız ilişkili verileri önden yükleyin.
- Büyük OFFSET —
LIMIT 20 OFFSET 100000ilk yüz bin satırı okuyup atar. Bunun yerine son görülen kimlikten devam eden sayfalama kullanın. LIKE '%kelime%'— baştaki joker indeksi kullanılamaz hale getirir. Metin araması için tam metin indeksi veya ayrı bir arama motoru gerekir.
Bellek ayarları
InnoDB tampon havuzu (innodb_buffer_pool_size), veri ve indeksleri bellekte
tutan asıl yapıdır. Yalnız veritabanı çalışan bir sunucuda toplam belleğin %60–70'i makul
bir başlangıçtır; web sunucusu ve PHP aynı makinedeyse bu oranı düşürün. Ayarı kör
büyütmeyin: takas alanına düşen bir veritabanı, küçük tampon havuzundan çok daha yavaştır.
Tampon havuzu isabet oranını ölçün. Sürekli diskten okuma yapılıyorsa ya havuz küçüktür ya da sorgular gereğinden fazla veri tarıyordur. İkinci ihtimali önce eleyin; indeks düzeltmesi bellek eklemekten hem ucuz hem kalıcıdır.
| Ayar | Makul başlangıç | Ne yapar |
|---|---|---|
innodb_buffer_pool_size | Adanmış sunucuda belleğin %60–70'i | Veri ve indeksleri bellekte tutar |
innodb_log_file_size | Tampon havuzunun dörtte biri | Yazma yoğun yükte disk baskısını düşürür |
innodb_flush_method | O_DIRECT | İşletim sistemiyle çift önbelleklemeyi önler |
max_connections | Gerçek eşzamanlılığın biraz üstü | Gereğinden yüksek değer bellek yer, hız getirmez |
İndeks ne zaman çözüm değildir?
Bazı yavaşlıklar indeksle düzelmez, çünkü sorun sorgunun kendisinde değildir:
- Sayfa başına onlarca sorgu. Her biri 2 ms sürse bile toplam 200 ms eder. Çözüm indeks değil, sonucu bellekte tutan bir önbellek katmanıdır; WordPress hızlandırma yazısındaki nesne önbelleği bölümü tam bu deseni anlatıyor.
- Yazma yoğun yük. Her yeni indeks, her
INSERTveUPDATEişlemine maliyet ekler. Günlük kaydı tutan tablolarda indeks sayısını asgaride tutun. - Raporlama sorguları. Milyonlarca satırı toplayan aylık ciro raporu, canlı tabloda çalıştırılmamalıdır. Gece hesaplanan bir özet tablo hem raporu anında açar hem canlı yükü rahatlatır.
- Yetersiz disk. Veri kümesi tampon havuzuna sığmıyorsa okuma hızını disk belirler. Böyle bir durumda NVMe'ye geçmek, indeks düzeltmesi kadar fark yaratır.
Bakım işleri
- Büyük silme işlemlerinden sonra tabloyu yeniden düzenleyin; boşluk kendiliğinden geri verilmez.
- İstatistikleri güncel tutun; planlayıcı yanlış istatistikle yanlış indeksi seçer.
- Karakter kümesini
utf8mb4yapın; eskiutf8dört baytlık karakterleri saklayamaz.
Veritabanı yükü paylaşımlı paketin sınırlarını zorluyorsa, ayrılmış kaynak sunan VDS veya cloud sunucu tarafına geçmek bellek ayarlarını kendiniz belirlemenize izin verir. Ne kadar kaynak gerektiğini tahmin etmek yerine ölçmek için kaynak planlama yazısına bakın; önce sorguları düzeltip sonra ölçmek, gereksiz büyütmenin önüne geçer.
- #mysql
- #indeks
- #veritabanı