İçeriğe geç
  • Hafta içi 08:00 – 18:00 · Destek 7/24
  1. Ana Sayfa
  2. Blog
  3. Performans

MySQL performansı: yavaş sorgudan doğru indekse

Veritabanı yavaşlığı tahminle çözülmez. Yavaş sorgu günlüğünü açmak, EXPLAIN çıktısını okumak ve indeksi doğru yerden eklemek.

"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:

  • typeALL tam tablo taraması demektir; küçük tablolar dışında kötüdür. ref, range ve const iyidir.
  • key — hangi indeksin kullanıldığı. NULL ise 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.
  • ExtraUsing filesort ve Using temporary ifadeleri sıralama ya da gruplama için geçici yapı kurulduğunu gösterir; genellikle doğru bileşik indeksle ortadan kalkar.
type değeriAnlamıDeğerlendirme
const, eq_refBirincil ya da tekil anahtarla tek satırEn iyi
refİndeksle sınırlı sayıda satırİyi
rangeİndeks üzerinde aralık taramasıKabul edilebilir
indexTüm indeksin baştan sona taranmasıŞüpheli, incelenmeli
ALLTam 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

  1. Sıra önemlidir. Bileşik indekste sütun sırası soldan sağa çalışır. (musteri_id, tarih) indeksi, yalnız musteri_id ile yapılan aramada da kullanılır; yalnız tarih ile yapılanda kullanılmaz.
  2. Sütunu sarmalamayın. WHERE YEAR(tarih) = 2026 yazarsanız indeks devre dışı kalır. Doğrusu aralık kullanmaktır: WHERE tarih >= '2026-01-01' AND tarih < '2027-01-01'.
  3. 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 OFFSETLIMIT 20 OFFSET 100000 ilk 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.

AyarMakul başlangıçNe yapar
innodb_buffer_pool_sizeAdanmış sunucuda belleğin %60–70'iVeri ve indeksleri bellekte tutar
innodb_log_file_sizeTampon havuzunun dörtte biriYazma yoğun yükte disk baskısını düşürür
innodb_flush_methodO_DIRECTİşletim sistemiyle çift önbelleklemeyi önler
max_connectionsGerç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 INSERT ve UPDATE iş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 utf8mb4 yapın; eski utf8 dö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ı
Bu yazıdaki adımları uygulayacak bir altyapı mı arıyorsunuz? Satış ekibimiz mevcut kurulumunuzu dinleyip uygun paketi önersin; kurulum ve taşıma bizde.
Devamı

İlgili yazılar

Performans

WordPress hızlandırma: önce ölçün, sonra dokunun

Eklenti yığmadan WordPress hızlandırmanın sırası: sunucu yanıtı, veritabanı, görseller, önbellek ve ön yüz. Her adımda ne ölçülür, hangi rakam iyidir?

  • 6 dk
Yazıyı oku
Performans

CDN ne zaman gerekir, ne zaman gereksizdir?

CDN her siteyi hızlandırmaz. Coğrafi dağılım, statik içerik oranı ve önbellek isabet oranına bakarak doğru kararı vermek.

  • 5 dk
Yazıyı oku
Performans

Sunucu izleme: neyi, hangi eşikle izlemeli?

İzlemenin amacı grafik biriktirmek değil, doğru anda uyandırmak. İzlenecek yedi metrik, gerçekçi eşikler ve alarm yorgunluğundan kaçınma.

  • 6 dk
Yazıyı oku