Rehber: MySQL Indexing
Veritabanı performansı söz konusu olduğunda, çoğu geliştiricinin aklına ilk gelen şey daha güçlü bir sunucuya geçmek ya da sorguları yeniden yazmak olur. Oysa gerçek şu ki, yavaş çalışan sorguların büyük bir kısmının tek bir ortak nedeni vardır: yanlış veya eksik indexleme. MySQL'de doğru kurgulanmış bir index yapısı, bazen donanımı iki katına çıkarmaktan çok daha fazla kazanç sağlar.
Bu rehberde MySQL indexlemenin ne olduğunu, nasıl çalıştığını, hangi durumlarda işe yaradığını ve hangi tuzaklardan kaçınmanız gerektiğini baştan sona ele alacağız. İster yeni başlayan bir geliştirici olun, ister yıllardır üretim ortamında çalışan bir DBA, buradaki prensipler her zaman geçerliliğini koruyacak.
Index Nedir ve Neden Bu Kadar Önemli?
Bir index, kabaca bir kitabın arkasındaki dizin gibidir. Kitabın tamamını baştan sona okumadan aradığınız konuyu bulmanızı sağlar. MySQL de aynı mantıkla çalışır: bir index olmadan, sorgunuz tablodaki her satırı tek tek taramak zorunda kalır. Buna full table scan denir ve veri büyüdükçe bu işlem felakete dönüşür.
MySQL'in varsayılan ve en yaygın kullandığı index yapısı B-Tree'dir. InnoDB motorunda bu yapı, verileri sıralı bir ağaç formunda tutar. Bu sayede arama, ekleme ve silme işlemleri logaritmik zaman karmaşıklığında (O(log n)) gerçekleşir. Yani 10 milyon satırlı bir tabloda bile bir kayda ulaşmak için yalnızca birkaç seviye ağaç dolaşmak yeterlidir.
Index'lerin sağladığı başlıca faydalar şunlardır:
SELECTsorgularında hız: ÖzellikleWHERE,JOINveORDER BYiçeren sorgularda dramatik iyileşme.- Sıralama ve gruplama maliyetinin düşmesi:
ORDER BYveGROUP BYişlemleri index üzerinden çok daha hızlı yapılır. - Benzersizlik kısıtları:
UNIQUEindex'ler veri bütünlüğünü garanti eder. - Foreign key ilişkilerinde performans: Join işlemlerinde kritik rol oynar.
Ancak her güzel şeyin bir bedeli olduğu gibi, index'lerin de maliyeti vardır. Her index, disk üzerinde ek yer kaplar ve her INSERT, UPDATE, DELETE işleminde güncellenmesi gerekir. Bu yüzden indexleme, "ne kadar çok o kadar iyi" mantığıyla yapılmaması gereken bir iştir.
MySQL'de Index Türleri
MySQL, farklı ihtiyaçlara cevap veren birden fazla index türü sunar. Bunları tanımak, doğru kararı vermenin ilk adımıdır.
Birincil Index (Primary Key)
Her InnoDB tablosunun bir clustered index'i vardır ve bu genellikle PRIMARY KEY'dir. Clustered index, verinin fiziksel olarak index sırasına göre saklandığı yapıdır. Bu yüzden birincil anahtar seçimi kritik önemdedir.
CREATE TABLE kullanicilar (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
olusturma_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP
);
Burada id sütunu hem benzersizliği garanti eder hem de verinin disk üzerindeki fiziksel sırasını belirler.
İkincil Index (Secondary Index)
Birincil anahtar dışındaki tüm index'ler ikincil index'tir. InnoDB'de ikincil index'ler, aradıkları değeri ve birincil anahtarı birlikte saklar. Bu detay, "covering index" konusunu anlamak için önemlidir.
CREATE INDEX idx_email ON kullanicilar(email);
Bileşik Index (Composite / Multi-Column Index)
Birden fazla sütunu tek bir index altında birleştiren yapıdır. Sorgu optimizasyonunda en güçlü araçlardan biridir, ancak sütun sırası hayati önem taşır.
CREATE INDEX idx_sehir_yas ON kullanicilar(sehir, yas);
Full-Text Index
Metin arama işlemleri için tasarlanmıştır. LIKE '%kelime%' gibi sorguların yerine çok daha verimli bir alternatif sunar.
CREATE FULLTEXT INDEX idx_icerik ON makaleler(baslik, icerik);
Hash Index
MySQL'de MEMORY motoru hash index destekler. Eşitlik karşılaştırmalarında (=) çok hızlıdır ancak aralık sorgularında (>, <, BETWEEN) işe yaramaz.
Bileşik Index'lerde Sütun Sırası Neden Önemli?
Bu konu, indexleme dünyasının en çok yanlış anlaşılan kısımlarından biridir. Bileşik bir index'te sütun sırası, index'in hangi sorgularda kullanılabileceğini doğrudan belirler. Buna leftmost prefix (en soldan başlayan önek) kuralı denir.
Diyelim ki şu index'e sahipsiniz:
CREATE INDEX idx_a_b_c ON tablo(a, b, c);
Bu index şu sorgularda kullanılabilir:
WHERE a = ?WHERE a = ? AND b = ?WHERE a = ? AND b = ? AND c = ?WHERE a = ? ORDER BY b
Ancak şu sorgularda kullanılamaz:
WHERE b = ?(a atlanmış)WHERE c = ?(a ve b atlanmış)WHERE b = ? AND c = ?(a atlanmış)
Bu kuralı akılda tutmanın basit bir yolu var: index'i soldan sağa doğru bir zincir gibi düşünün. Zincirin bir halkası eksikse, sonraki halkalar işe yaramaz.
Bu yüzden bileşik index oluştururken sütunları kardinaliteye (benzersiz değer sayısına) göre değil, sorgu deseninize göre sıralamalısınız. En sık filtrelenen ve en seçici sütunu en sola koymak genellikle iyi bir başlangıçtır.
Covering Index: Sorguyu Tabloya Gitmeden Bitirmek
Covering index, bir sorgunun ihtiyaç duyduğu tüm sütunları index içinde barındıran index'tir. Bu durumda MySQL, index üzerinden sonucu döndürebilir ve asıl tabloya hiç dokunmaz. Bu, özellikle büyük tablolarda ciddi bir performans kazancı sağlar.
CREATE INDEX idx_kapak ON siparisler(musteri_id, durum, toplam_tutar);
SELECT toplam_tutar
FROM siparisler
WHERE musteri_id = 42 AND durum = 'tamamlandi';
Bu sorguda MySQL, idx_kapak index'ini kullanarak tabloya hiç erişmeden sonucu döndürebilir. EXPLAIN çıktısında Extra sütununda Using index ifadesini görürseniz, covering index devrede demektir.
EXPLAIN ile Index Kullanımını Analiz Etmek
Index'lerinizin gerçekten çalışıp çalışmadığını anlamanın tek güvenilir yolu, sorgularınızı EXPLAIN ile analiz etmektir.
EXPLAIN SELECT * FROM kullanicilar WHERE email = 'ornek@mail.com';
Çıktıda dikkat etmeniz gereken sütunlar:
type: Erişim tipi.const,eq_ref,ref,rangeiyi;ALL(full scan) kötüdür.key: Kullanılan index'in adı.NULLise index kullanılmıyor.rows: Taranan tahmini satır sayısı. Düşük olması iyidir.Extra:Using index(covering index),Using where,Using filesort(kötü) gibi ipuçları.
MySQL 8.0 ile gelen EXPLAIN ANALYZE komutu ise tahmin yerine gerçek çalışma istatistiklerini verir:
EXPLAIN ANALYZE SELECT * FROM siparisler WHERE musteri_id = 42;
Index'in İşe Yaramadığı Durumlar
Index her derde deva değildir. Aşağıdaki durumlarda index devre dışı kalır ve boşuna yer kaplarsınız:
- Fonksiyon uygulanan sütunlar:
WHERE YEAR(tarih) = 2024yerineWHERE tarih >= '2024-01-01' AND tarih < '2025-01-01'yazın. LIKEile baştan joker karakter:LIKE '%kelime'index kullanamaz,LIKE 'kelime%'kullanabilir.- Tip uyumsuzlukları:
VARCHARbir sütunu sayı ile karşılaştırmak index'i devre dışı bırakır. ORkoşulları: Bazı durumlarda optimizasyon zorlaşır;UNIONdaha verimli olabilir.- Düşük kardinaliteli sütunlar: Cinsiyet gibi yalnızca 2-3 farklı değer alan sütunlarda index genellikle işe yaramaz.
- Negatif koşullar:
!=,NOT INgibi ifadeler index kullanımını engelleyebilir.
Index Stratejisi Oluştururken Dikkat Edilmesi Gerekenler
Bir tablo için index stratejisi belirlerken şu prensipleri göz önünde bulundurun:
- Önce sorguları analiz edin, sonra index yazın. Hangi sorguların yavaş çalıştığını bilmeden index eklemek kumar oynamaktır.
- Yavaş sorgu logunu açın.
slow_query_logparametresini aktif ederek gerçek darbogazları tespit edin. - Gereksiz index'lerden kaçının. Her index yazma maliyetini artırır.
SHOW INDEX FROM tablo;ile mevcut index'leri düzenli olarak gözden geçirin. - Bileşik index'lerde sütun sırasını sorgu deseninize göre belirleyin. Leftmost prefix kuralını unutmayın.
- Covering index fırsatlarını değerlendirin. Özellikle raporlama sorgularında büyük fark yaratır.
- Birincil anahtar seçiminde dikkatli olun.
AUTO_INCREMENT BIGINTgenellikle en iyi tercihtir; rastgele UUID'ler InnoDB'de parçalanmaya yol açar. - Index'leri düzenli olarak yeniden değerlendirin. Uygulama geliştikçe sorgu desenleri değişir; eski index'ler işe yaramaz hale gelebilir.
Sık Yapılan Hatalar
Deneyimlerime göre geliştiricilerin en sık düştüğü tuzaklar şunlardır:
- Her sütuna index eklemek. Bu, yazma performansını yerle bir eder ve optimizasyonu zorlaştırır.
- Index'lerin bakım gerektirmediğini sanmak. Fragmentasyon zamanla oluşur;
OPTIMIZE TABLE(InnoDB'de dikkatli kullanılmalı) veya tablo yeniden oluşturma gerekebilir. EXPLAINçıktısını okumadan "index ekledim, artık hızlıdır" demek. Ölçmeden iyileştirme olduğunu varsaymak profesyonel değildir.- Test ortamında küçük veriyle karar vermek. 1000 satırlı bir tabloda full scan zaten hızlıdır; gerçek performans farkı milyonlarca satırda ortaya çıkar.
- Bileşik index'te sütun sırasını rastgele seçmek. Bu, index'i büyük ölçüde işe yaramaz hale getirir.
Sonuç
MySQL'de indexleme, hem bir bilim hem de bir sanattır. Doğru kurgulandığında sorgularınız milisaniyeler içinde yanıt verir; yanlış kurgulandığında ise hem disk hem de CPU kaynaklarınız boşa harcanır. Bu rehberdeki prensipleri uygulamak için sihirli bir formül yok; her uygulamanın sorgu desenleri farklıdır. Ancak EXPLAIN kullanmayı alışkanlık haline getirir, leftmost prefix kuralını aklınızda tutar ve index'leri sürekli gözden geçirirseniz, veritabanınızın performansını ciddi ölçüde artırabilirsiniz.
Unutmayın: en iyi index, ihtiyacınız olanı karşılayan ve gereksiz olanı barındırmayan index'tir. Ölçün, analiz edin ve kararlarınızı veriye dayandırın. Veritabanınız size teşekkür edecek.