Rehber: MySQL Indexing — Veritabanı Performansının Görünmez Mimarı
Bir MySQL veritabanı, ilk günlerinde ne kadar hızlı olursa olsun, veri hacmi büyüdükçe yavaşlar. Bu yavaşlamanın arkasındaki en yaygın sebep, yanlış veya eksik indekslemedir. Peki indeks nedir, nasıl çalışır ve doğru şekilde nasıl tasarlanır? Bu rehberde MySQL indekslerini hem teorik hem de pratik yönleriyle ele alacağız.
Index Nedir ve Neden Bu Kadar Önemli?
Bir indeks, kitabın sonundaki dizin gibi düşünülebilir. 500 sayfalık bir kitapta "MySQL" kelimesini aramak yerine dizine bakıp ilgili sayfaya atlarsınız. MySQL de aynı mantıkla çalışır: indeks olmadan tüm satırları tek tek tarar (full table scan), indeksle ise doğrudan ilgili veriye ulaşır.
Küçük tablolarda bu fark hissedilmez. Ancak milyonlarca satır söz konusu olduğunda indeksli bir sorgu milisaniyeler içinde dönerken, indekssiz sorgu saniyeler, hatta dakikalar sürebilir.
İndeksin Sağladığı Kazanımlar
- Hızlı SELECT sorguları: WHERE, JOIN ve ORDER BY işlemlerinde ciddi hız artışı
- Etkin sıralama: ORDER BY ve GROUP BY operasyonlarının hızlandırılması
- Tekillik garantisi: UNIQUE indekslerle veri tutarlılığı
- JOIN performansı: İlişkili tablolar arasında hızlı eşleştirme
İndeksin Maliyeti
Her şeyin bir bedeli olduğu gibi indekslerin de vardır:
- Disk alanı tüketimi: Her indeks ekstra yer kaplar
- Yazma yavaşlaması: INSERT, UPDATE, DELETE işlemlerinde indekslerin de güncellenmesi gerekir
- Bakım yükü: Gereksiz indeksler sorgu optimizasyonunu zorlaştırır
MySQL'de İndeks Türleri
MySQL, ihtiyaca göre farklı indeks türleri sunar. Doğru türü seçmek, performansın anahtarıdır.
1. B-Tree Index (Varsayılan)
Çoğu depolama motorunda (InnoDB, MyISAM) varsayılan indeks türüdür. Dengeli ağaç yapısı kullanır ve eşitlik, aralık, sıralama sorgularında etkilidir.
CREATE INDEX idx_email ON users(email);
2. Hash Index
Yalnızca eşitlik karşılaştırmalarında kullanılır. MEMORY motorunda doğal olarak bulunur, InnoDB'de ise "adaptive hash index" olarak otomatik devreye girer.
3. Full-Text Index
Metin arama için tasarlanmıştır. MATCH ... AGAINST sözdizimiyle kullanılır.
CREATE FULLTEXT INDEX idx_content ON articles(title, body);
SELECT * FROM articles WHERE MATCH(title, body) AGAINST('mysql performans');
4. Spatial Index
Geometrik veri tipleri (POINT, POLYGON) için kullanılır. Harita tabanlı uygulamalarda kritiktir.
5. Composite (Bileşik) Index
Birden fazla sütunu tek indekste birleştirir. Doğru sıralama ile kullanıldığında çok güçlüdür.
CREATE INDEX idx_user_status ON orders(user_id, status, created_at);
İndeks Oluşturma: Temel Sözdizimi
CREATE INDEX ile
CREATE INDEX idx_lastname ON employees(last_name);
CREATE UNIQUE INDEX idx_username ON users(username);
ALTER TABLE ile
ALTER TABLE products ADD INDEX idx_price (price);
ALTER TABLE products ADD UNIQUE INDEX idx_sku (sku);
Tablo Tanımlarken
CREATE TABLE customers (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL,
city VARCHAR(100),
created_at DATETIME,
INDEX idx_city (city),
UNIQUE INDEX idx_email (email)
);
PRIMARY KEY, UNIQUE ve Normal Index Farkları
| Özellik | PRIMARY KEY | UNIQUE | INDEX |
|---|---|---|---|
| Tekillik | Zorunlu | Zorunlu | Yok |
| NULL değer | Kabul etmez | Kabul eder (birden fazla) | Kabul eder |
| Tablo başına | 1 adet | Birden fazla | Birden fazla |
| Kümelenmiş (InnoDB) | Evet | Hayır | Hayır |
InnoDB'de PRIMARY KEY kümelenmiş indekstir; yani tablo verisi doğrudan bu indeksin yapraklarında saklanır. Bu yüzden birincil anahtar seçimi kritik önem taşır.
Bileşik İndekslerde Sütun Sırası: En Kritik Detay
Bileşik indekslerde sütun sırası, indeksin kullanılabilirliğini doğrudan etkiler. Sol önek (leftmost prefix) kuralı geçerlidir.
CREATE INDEX idx_a_b_c ON tablo(a, b, c);
Bu indeks şu sorgularda kullanılır:
WHERE a = ?WHERE a = ? AND b = ?WHERE a = ? AND b = ? AND c = ?
Ancak şu sorgularda kullanılmaz:
WHERE b = ?(a atlanmış)WHERE c = ?(a ve b atlanmış)WHERE b = ? AND c = ?
Bu nedenle indeks tasarlarken en sık kullanılan ve en seçici sütunu en başa koymak gerekir.
Covering Index (Kapsayıcı İndeks)
Bir sorgunun ihtiyaç duyduğu tüm sütunlar indekste mevcutsa, MySQL tabloya hiç dokunmadan yanıt üretebilir. Buna "covering index" denir ve ciddi performans kazancı sağlar.
CREATE INDEX idx_cover ON orders(user_id, status, total);
SELECT user_id, status, total FROM orders WHERE user_id = 5;
Burada sorgu yalnızca indeksten okunur; tablo erişimi olmaz.
EXPLAIN ile İndeks Kullanımını Analiz Etmek
Bir sorgunun indeksi kullanıp kullanmadığını görmek için EXPLAIN komutu hayat kurtarır.
EXPLAIN SELECT * FROM users WHERE email = 'ornek@mail.com';
Çıktıdaki type alanına dikkat edin:
const/eq_ref→ Mükemmelref/range→ İyiindex→ Tüm indeks taranıyorALL→ Full table scan, tehlikeli
possible_keys ve key alanları, hangi indeksin aday olduğunu ve hangisinin seçildiğini gösterir. rows alanı ise taranan tahmini satır sayısıdır.
İndekslerin Kullanılmadığı Durumlar
İndeks olsa bile bazı sorgular onu kullanamaz:
- Sütun üzerinde fonksiyon:
WHERE YEAR(created_at) = 2024 - Baştaki joker karakter:
WHERE name LIKE '%ahmet' - Tip uyumsuzluğu:
WHERE user_id = '5'(sayısal sütuna string) - OR koşulları (her iki tarafta indeks yoksa)
- Düşük kardinalite:
WHERE gender = 'E'(neredeyse tüm satırlar aynı)
Çözüm: Fonksiyonu sütundan kaldırın, joker karakteri sona alın, tip uyumunu sağlayın.
İndeks Bakımı ve Optimizasyonu
İndeksler zamanla "şişebilir". Düzenli bakım önemlidir.
İndeks Kullanımını İzleme
SELECT * FROM sys.schema_unused_indexes;
SELECT * FROM sys.schema_redundant_indexes;
Bu görünümler, hiç kullanılmayan veya gereksiz indeksleri tespit etmenizi sağlar.
İndeks İstatistiklerini Güncelleme
ANALYZE TABLE users;
İndeks Silme
DROP INDEX idx_eski ON users;
ALTER TABLE users DROP INDEX idx_eski;
İndeks Yeniden Oluşturma
InnoDB'de OPTIMIZE TABLE indeksleri yeniden düzenler:
OPTIMIZE TABLE orders;
İndeks Tasarımında Altın Kurallar
- Az ama öz: Her sütuna indeks eklemek performansı düşürür, artırmaz.
- Kardinaliteye dikkat: Yüksek kardinaliteli (çok farklı değer içeren) sütunlar önceliklidir.
- Bileşik indekste sıra önemli: En seçici ve en sık sorgulanan sütun başa gelir.
- FOREIGN KEY sütunlarını indeksleyin: JOIN performansı için kritik.
- WHERE + ORDER BY kombinasyonunu düşünün: İkisini de karşılayan indeks ideal.
- Covering index fırsatlarını değerlendirin: SELECT sütunlarını indekse dahil edin.
- Kullanılmayan indeksleri temizleyin: Yazma yükünü azaltır.
- EXPLAIN alışkanlık haline gelsin: Her kritik sorguyu analiz edin.
Sık Yapılan Hatalar
- Her sütuna ayrı indeks eklemek (indeks enflasyonu)
- Bileşik indekste yanlış sıralama yapmak
- Düşük kardinaliteli sütunları indekslemek
- Sorgularda sütun üzerinde fonksiyon kullanmak
- İndekslerin bakımını ihmal etmek
- Test ortamında veri olmadan indeks kararı vermek
Sonuç
MySQL indeksleme, veritabanı performansının görünmez ama en güçlü mimarıdır. Doğru tasarlanmış birkaç indeks, yüzlerce satır kod optimizasyonundan daha fazla kazanç sağlayabilir. Ancak indeksler sihirli değnek değildir; yanlış kullanıldığında yazma performansını düşürür ve disk alanını israf eder.
Özetle: Sorgularınızı tanıyın, EXPLAIN ile analiz edin, bileşik indekslerde sıralamayı doğru yapın ve kullanılmayan indeksleri temizleyin. Bu disiplinli yaklaşım, veritabanınızın ölçek büyüdükçe de hızlı kalmasını sağlayacaktır.
Unutmayın: En iyi indeks, sorgunuzun gerçekten ihtiyaç duyduğu indekstir — ne bir fazlası, ne bir eksiği.