aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Oyun Sunucusu

MySQL Index Optimizasyonu: Yavaş Sorguları Çözün

Doğru kullanıldığında bir MySQL index, saniyelerce süren bir sorguyu milisaniyelere indirir; yanlış kullanıldığında ise hiç fayda etmez, hatta yazma işlemlerini yavaşlatır. Özellikle oyun sunucularında oturum, log, envanter ve sıralama tabloları hızla milyonlarca satıra ulaşır. Bu noktada darboğazı tahmin etmek yerine ölçmek gerekir. Bu rehberde yavaş sorguları EXPLAIN ile teşhis etmeyi, hangi sütunları indekslemen gerektiğine karar vermeyi ve sık yapılan hataları nasıl önleyeceğini adım adım göreceğiz.

Index neden işe yarar?

Index olmayan bir tabloda MySQL, aradığın satırı bulmak için tüm tabloyu baştan sona tarar (full table scan). Tablo büyüdükçe bu doğrusal olarak yavaşlar. Index ise, çoğu durumda bir B-tree yapısıdır: arama yaptığın sütunun değerlerini sıralı tutar ve binary search benzeri bir mantıkla MySQL'in milyonlarca satır arasında doğrudan ilgili bloğa atlamasını sağlar.

Bunun bir bedeli vardır: her INSERT, UPDATE ve DELETE işleminde index de güncellenir. Yani index, okumayı hızlandırırken yazmayı bir miktar yavaşlatır ve disk alanı tüketir. Bu yüzden hedef, "her şeyi indeksle" değil; doğru sütunları indekslemektir.

Yavaş sorguyu bulmak: slow query log

Önce hangi sorgunun yavaş olduğunu öğrenmen lazım. MySQL'in slow query log özelliği, belirlediğin eşikten uzun süren sorguları kaydeder. Oturum bazında hızlıca açabilirsin:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;        -- 1 saniyeden uzun sorgular
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

Bir süre çalıştıktan sonra mysqldumpslow aracıyla en sık ve en yavaş sorguları özetleyebilirsin:

mysqldumpslow -s t /var/log/mysql/slow.log | head -20

Buradaki amaç, optimize edilecek somut bir sorgu cümlesi bulmaktır. Tahminle değil, gerçek trafikle çalış.

EXPLAIN ile teşhis

Elinde yavaş bir sorgu olduğunda, MySQL'in onu nasıl çalıştırdığını EXPLAIN ile görürsün. Örnek bir oyun sunucusu sorgusu:

EXPLAIN SELECT * FROM player_logs
WHERE account_id = 4815 AND action = 'login'
ORDER BY created_at DESC
LIMIT 20;

Çıktıda en çok şu sütunlara bakarsın:

  • type: erişim yöntemi. ALL kötüdür (tam tarama), ref ve range iyidir, const/eq_ref en iyisidir.
  • key: MySQL'in gerçekten kullandığı index. NULL ise hiç index kullanılmıyor demektir.
  • rows: MySQL'in tarayacağını tahmin ettiği satır sayısı. Düşük olması iyidir.
  • Extra: Using filesort veya Using temporary görüyorsan, sıralama/gruplama için ek iş yapılıyor demektir.

Daha ayrıntılı plan için EXPLAIN ANALYZE (MySQL 8.0.18+) sorguyu gerçekten çalıştırıp ölçülen süreleri gösterir — tahmin yerine gerçek maliyeti görürsün.

Doğru index'i tasarlamak

Yukarıdaki sorguda account_id ve action ile filtreleyip created_at ile sıralıyoruz. İdeal index, tam olarak bu sırayı yansıtan bir bileşik (composite) indextir:

CREATE INDEX idx_logs_acc_action_time
  ON player_logs (account_id, action, created_at);

Burada kritik kavram en soldan ön ek (leftmost prefix) kuralıdır. Bileşik bir index (a, b, c) şeklindeyse; MySQL onu a, a+b veya a+b+c aramaları için kullanabilir; ama tek başına b veya c için kullanamaz. Bu yüzden sütun sırası önemlidir:

  • Önce eşitlik (=) filtreleri gelir (burada account_id, action).
  • Sonra aralık (range) ve sıralama (ORDER BY) sütunu gelir (created_at).

Bu sıralama sayesinde MySQL hem filtrelemeyi hem de sıralamayı index üzerinden yapar ve Using filesort kaybolur.

Sık yapılan hatalar

  • Sütuna fonksiyon uygulamak: WHERE DATE(created_at) = '2026-06-27' yazarsan index kullanılamaz. Bunun yerine aralık kullan: WHERE created_at >= '2026-06-27' AND created_at < '2026-06-28'.
  • Sol tarafta joker: LIKE '%abc' index'i devre dışı bırakır; LIKE 'abc%' ise kullanabilir.
  • Tip uyuşmazlığı: sayısal bir sütunu WHERE id = '123' gibi string ile karşılaştırmak örtük dönüşüme ve tam taramaya yol açabilir.
  • Aşırı indeksleme: nadiren sorgulanan ya da düşük seçicilikli (örneğin sadece 0/1 değerli) sütunları tek başına indekslemek genelde fayda etmez.
  • Gereksiz SELECT *: sadece ihtiyacın olan sütunları seçersen, bazen index'in kendisi tüm veriyi karşılar (covering index) ve tabloya hiç gidilmez.

Doğrula ve bakımını yap

Index ekledikten sonra aynı EXPLAIN'i tekrar çalıştır: type ALL'dan ref/range'e geçmeli, rows ciddi düşmeli ve filesort kaybolmalı. İstatistiklerin güncel kalması için ara sıra ANALYZE TABLE player_logs; çalıştırabilirsin. Kullanılmayan index'leri ise sys.schema_unused_indexes görünümüyle tespit edip kaldırarak yazma yükünü azaltabilirsin.

Unutma: index optimizasyonu tek seferlik bir iş değil, sürekli bir döngüdür — ölç, indeksle, doğrula, tekrarla.

Sık Sorulan Sorular

Bir tabloya kaç index ekleyebilirim?

Teknik sınır yüksek olsa da pratik sınır iş yüküne bağlıdır. Çok yazma alan tablolarda az ve isabetli index tut; çok okuma alan raporlama tablolarında daha fazla index makul olabilir. Her index'in bir yazma ve depolama maliyeti olduğunu unutma.

Tek tek index mi, bileşik index mi daha iyi?

Bir sorgu birden çok sütunu birlikte filtreliyorsa, doğru sıralanmış tek bir bileşik index, ayrı ayrı index'lerden neredeyse her zaman daha iyidir. MySQL çoğu durumda sorgu başına tek bir index'i en verimli şekilde kullanır.

Primary key zaten index mi?

Evet. InnoDB'de primary key aynı zamanda kümeleyici (clustered) index'tir ve satır verisini fiziksel olarak ona göre saklar. Bu yüzden kısa, sıralı ve değişmeyen bir primary key (örneğin auto-increment) seçmek performans açısından önemlidir.

Veritabanın yavaşladığında tahminle uğraşmayı bırak. Oyun sunucusu ya da web uygulaması fark etmez; yavaş sorgularını birlikte ölçüp doğru index stratejisini kuralım. Benimle iletişime geç ve veritabanını hızlandıralım.

Bu kategorideki tüm yazılar →

Devamı için