MySQL Indexing: Veritabanı Performans Rehberi

# MySQL Indexing: Veritabanı Performansının Görünmez Mimarı Veritabanı yönetimiyle uğraşan herkesin bir noktada fark ettiği bir gerçek var: Sorguları...

MySQL Indexing: Veritabanı Performans Rehberi

MySQL Indexing: Veritabanı Performansının Görünmez Mimarı

Veritabanı yönetimiyle uğraşan herkesin bir noktada fark ettiği bir gerçek var: Sorgularınız yavaşladığında, sorun genellikle donanımda ya da kodunuzda değil, indekslerinizde saklıdır. MySQL, dünyanın en yaygın kullanılan açık kaynak ilişkisel veritabanı yönetim sistemlerinden biri ve onu verimli kullanmanın anahtarı, indeksleme stratejisini doğru kurgulamaktan geçiyor.

Bu rehberde, MySQL indekslerinin ne olduğundan başlayıp, nasıl çalıştıklarına, hangi türlerinin bulunduğuna ve gerçek dünya senaryolarında nasıl optimize edileceğine kadar kapsamlı bir yolculuğa çıkacağız. İster yeni başlayan bir geliştirici olun, ister yıllardır veritabanı yöneten bir DBA, burada işinize yarayacak pratik bilgiler bulacaksınız.

İndeks Nedir ve Neden Bu Kadar Kritik?

Bir indeks, en basit tanımıyla, veritabanı tablosundaki verilere daha hızlı erişmek için kullanılan özel bir veri yapısıdır. Kitabınızın sonundaki dizini düşünün: Belirli bir konuyu aradığınızda, tüm sayfaları tek tek karıştırmak yerine dizine bakıp ilgili sayfaya doğrudan atlarsınız. MySQL indeksleri de tam olarak bu mantıkla çalışır.

İndeks olmadan, MySQL bir sorguyu çalıştırırken tablodaki her satırı tek tek okumak zorunda kalır. Buna "full table scan" (tam tablo taraması) denir. Milyonlarca satır içeren bir tabloda bu işlem saniyeler, hatta dakikalar sürebilir. İndeksler sayesinde MySQL, aradığı veriye doğrudan ulaşır ve bu süre milisaniyelere iner.

Ancak indeksler sihirli değnek değildir. Her indeks disk alanı kaplar, her INSERT, UPDATE ve DELETE işleminde ek yük getirir. Bu yüzden "ne kadar çok indeks, o kadar iyi" yaklaşımı yanlıştır. Doğru strateji, hangi sorguların hangi indekslere ihtiyaç duyduğunu analiz etmekten geçer.

MySQL İndekslerinin İç Yapısı: B-Tree ve Diğerleri

MySQL'in varsayılan depolama motoru InnoDB, indeksleri genellikle B-Tree (aslında B+Tree) veri yapısıyla saklar. Bu yapı, dengeli bir ağaç oluşturarak arama, ekleme ve silme işlemlerini logaritmik zaman karmaşıklığında gerçekleştirir.

B-Tree İndekslerin Çalışma Prensibi

B-Tree indeksinde veriler sıralı olarak tutulur. Bu sıralama sayesinde MySQL şu işlemleri çok verimli yapabilir:

  • Eşitlik aramaları: WHERE id = 42 gibi sorgular
  • Aralık aramaları: WHERE tarih BETWEEN '2024-01-01' AND '2024-12-31'
  • Sıralama işlemleri: ORDER BY ifadeleri indeks sırasına uyuyorsa ek sıralama gerekmez
  • Önek aramaları: WHERE isim LIKE 'Ahmet%' gibi sol taraftan eşleşen aramalar

Hash İndeksler

MEMORY depolama motoru hash indeksleri kullanır. Hash indeksler eşitlik aramalarında son derece hızlıdır ancak aralık sorgularında işe yaramaz. Çünkü hash fonksiyonu verileri sıralı tutmaz, sadece anahtar-değer eşleşmesi sağlar.

Full-Text İndeksler

Metin aramaları için özel olarak tasarlanmış indeks türüdür. MATCH ... AGAINST sözdizimiyle kullanılır ve blog yazıları, ürün açıklamaları gibi uzun metin alanlarında kelime bazlı arama yapmanızı sağlar.

İndeks Türleri: Tek Sütundan Karmaşığa

MySQL'de karşımıza çıkan temel indeks türlerini sıralayalım:

Birincil Anahtar (Primary Key) İndeksi

Her InnoDB tablosunun bir clustered index'i vardır ve bu genellikle birincil anahtardır. Clustered index, verinin fiziksel olarak indeks sırasına göre disk üzerinde saklandığı anlamına gelir. Bu yüzden birincil anahtar seçimi kritik önem taşır.

CREATE TABLE kullanicilar (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    kayit_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP
);

İkincil (Secondary) İndeksler

Birincil anahtar dışındaki tüm indeksler ikincil indeks olarak adlandırılır. Bu indeksler, indekslenen sütun değerini ve birincil anahtarı birlikte saklar. Bu yüzden bir sorguda ikincil indeks kullanıldığında, MySQL önce indekse bakar, sonra birincil anahtar üzerinden asıl veriye ulaşır. Buna "bookmark lookup" denir.

Bileşik (Composite) İndeksler

Birden fazla sütunu tek bir indekste birleştiren yapılardır. Sıralama son derece önemlidir çünkü MySQL indeksi soldan sağa doğru kullanır.

CREATE INDEX idx_isim_tarih ON siparisler (musteri_id, siparis_tarihi);

Bu indeks şu sorguları hızlandırır:

  • WHERE musteri_id = 5
  • WHERE musteri_id = 5 AND siparis_tarihi > '2024-01-01'

Ancak sadece WHERE siparis_tarihi > '2024-01-01' şeklindeki bir sorgu bu indeksten faydalanamaz. Bu kurala "leftmost prefix" (en soldaki önek) prensibi denir.

Kapsayıcı (Covering) İndeksler

Bir sorgunun ihtiyaç duyduğu tüm sütunları içeren indekslerdir. Bu durumda MySQL, asıl tabloya hiç bakmadan sadece indeksten sonucu döndürebilir. Bu, ciddi bir performans kazancı demektir.

-- Bu sorgu için kapsayıcı indeks
CREATE INDEX idx_covering ON siparisler (musteri_id, siparis_tarihi, toplam_tutar);

SELECT musteri_id, siparis_tarihi, toplam_tutar 
FROM siparisler 
WHERE musteri_id = 5;

İndeks Oluşturma ve Yönetme

İndeks Ekleme

-- Tek sütunlu indeks
CREATE INDEX idx_email ON kullanicilar (email);

-- Bileşik indeks
CREATE INDEX idx_ad_soyad ON kullanicilar (ad, soyad);

-- UNIQUE indeks
CREATE UNIQUE INDEX idx_unique_email ON kullanicilar (email);

-- ALTER TABLE ile ekleme
ALTER TABLE kullanicilar ADD INDEX idx_telefon (telefon);

İndeks Silme

DROP INDEX idx_email ON kullanicilar;
-- veya
ALTER TABLE kullanicilar DROP INDEX idx_email;

Mevcut İndeksleri Görüntüleme

SHOW INDEX FROM kullanicilar;
SHOW CREATE TABLE kullanicilar;

İndeks Performansını Ölçme: EXPLAIN Komutu

İndekslerin gerçekten kullanılıp kullanılmadığını anlamanın en güvenilir yolu EXPLAIN komutudur. Bu komut, MySQL'in sorgunuzu nasıl çalıştırmayı planladığını gösterir.

EXPLAIN SELECT * FROM siparisler WHERE musteri_id = 100;

Çıktıda dikkat etmeniz gereken alanlar:

  • type: Sorgu tipi. ALL görüyorsanız tam tablo taraması yapılıyor demektir, bu kötüdür. ref, range, const gibi değerler iyidir.
  • possible_keys: Kullanılabilecek indeksler
  • key: Gerçekten kullanılan indeks
  • rows: Taranacak tahmini satır sayısı
  • Extra: Ek bilgiler. Using index görüyorsanız kapsayıcı indeks çalışıyor demektir. Using filesort veya Using temporary görüyorsanız optimizasyon gerekebilir.

İndeks Stratejisi Oluşturmanın Altın Kuralları

Doğru indeks stratejisi, uygulamanızın sorgu desenlerini anlamaktan geçer. İşte dikkat etmeniz gereken temel prensipler:

1. WHERE, JOIN ve ORDER BY Sütunlarını Önceliklendirin

Sorgularınızda en sık kullanılan filtreleme ve birleştirme sütunlarına indeks ekleyin. Ancak gereksiz indekslerden kaçının.

2. Seçiciliği Yüksek Sütunları İndeksleyin

Bir sütundaki farklı değerlerin toplam satır sayısına oranına seçicilik (cardinality) denir. Cinsiyet gibi sadece iki farklı değer alan bir sütuna indeks eklemek genellikle anlamsızdır. E-posta, kullanıcı adı gibi sütunlar yüksek seçiciliğe sahiptir ve indekslenmeye değerdir.

3. Bileşik İndekslerde Sıralamayı Doğru Yapın

En sık filtrelenen sütun başa, daha az filtrelenen sütunlar sona gelmelidir. Ayrıca eşitlik karşılaştırması yapılan sütunlar, aralık karşılaştırması yapılanlardan önce gelmelidir.

4. Kapsayıcı İndeksleri Değerlendirin

Sık çalışan kritik sorgular için kapsayıcı indeksler oluşturmak, disk I/O'sunu ciddi ölçüde azaltabilir.

5. Yazma Yoğun Tablolarda İndeks Sayısını Sınırlayın

Her indeks, yazma işlemlerini yavaşlatır. Yoğun INSERT yapılan log tablolarında minimum indeks kullanmak daha akıllıca olabilir.

Yaygın İndeks Hataları ve Çözümleri

Hata 1: Fonksiyon Kullanımı İndeksi Devre Dışı Bırakır

-- Kötü: İndeks kullanılmaz
SELECT * FROM siparisler WHERE YEAR(siparis_tarihi) = 2024;

-- İyi: İndeks kullanılır
SELECT * FROM siparisler 
WHERE siparis_tarihi >= '2024-01-01' 
AND siparis_tarihi < '2025-01-01';

Bir sütuna fonksiyon uyguladığınızda, MySQL indeksi kullanamaz çünkü indeksteki değerler fonksiyonun sonucunu değil, ham değeri saklar.

Hata 2: LIKE Sorgusunda Baştaki Joker Karakteri

-- Kötü: İndeks kullanılmaz
SELECT * FROM urunler WHERE ad LIKE '%laptop%';

-- İyi: İndeks kullanılır
SELECT * FROM urunler WHERE ad LIKE 'laptop%';

Başta % olması, indeksin sıralı yapısını bozar ve tam tarama gerektirir.

Hata 3: Örtük Tip Dönüşümleri

-- telefon sütunu VARCHAR ise
-- Kötü: Tip dönüşümü nedeniyle indeks kullanılmaz
SELECT * FROM kullanicilar WHERE telefon = 5551234567;

-- İyi
SELECT * FROM kullanicilar WHERE telefon = '5551234567';

Hata 4: Gereksiz İndeksler

Kullanılmayan indeksler sadece disk alanı kaplamaz, aynı zamanda yazma performansını düşürür. performance_schema ve sys şemasındaki görünümleri kullanarak kullanılmayan indeksleri tespit edebilirsiniz:

SELECT * FROM sys.schema_unused_indexes;

İleri Seviye Konular

Kısmi (Prefix) İndeksler

Uzun VARCHAR sütunlarında tüm değeri indekslemek yerine belirli bir önek uzunluğunu indeksleyebilirsiniz:

CREATE INDEX idx_url_prefix ON sayfalar (url(50));

Bu yaklaşım disk alanından tasarruf sağlar ancak seçiciliği düşürebilir.

Görünmez (Invisible) İndeksler

MySQL 8.0 ile gelen bu özellik, bir indeksi silmeden devre dışı bırakmanıza olanak tanır. Böylece indeksin gerçekten gerekli olup olmadığını güvenle test edebilirsiniz:

ALTER TABLE kullanicilar ALTER INDEX idx_email INVISIBLE;

Çoklu Değerli (Multi-Valued) İndeksler

JSON sütunları içindeki diziler için özel indeksler oluşturabilirsiniz:

CREATE INDEX idx_etiketler ON urunler ((CAST(etiketler AS CHAR(50) ARRAY)));

Sonuç: İndeksleme Bir Sanattır

MySQL indeksleme, ne yalnızca teorik bilgiyle ne de sadece deneme yanılmayla öğrenilebilecek bir konu. Her ikisini birleştirmeniz gerekiyor. Sorgularınızı profilleyin, EXPLAIN çıktılarını okuyun, gerçek trafik altında testler yapın ve indekslerinizi sürekli gözden geçirin.

Unutmayın: En iyi indeks, uygulamanızın gerçek sorgu desenlerine göre şekillenen ve zaman içinde evrilen indekstir. Uygulamanız büyüdükçe, veri hacminiz arttıkça ve kullanım alışkanlıkları değiştikçe, indeks stratejinizi de güncellemeniz gerekir. Bu, tek seferlik bir görev değil, sürekli bir bakım sürecidir.

Veritabanı performansı söz konusu olduğunda indeksler, sessiz ama güçlü kahramanlardır. Onları doğru anladığınızda ve akıllıca kullandığınızda, uygulamanızın hızı ve ölçeklenebilirliği bambaşka bir seviyeye taşınır.

İLGİLİ İÇERİKLER

Bunları da Okumak İsteyebilirsiniz