SQL INDEX STRATEJİLERİ

SQL index stratejileri için B-tree ağaç yapısı kök düğümden yaprak düğümlere dallanma diyagramı

Bir sorguyu hızlandırmak için B-tree index mi yeterli, yoksa bitmap index'e mi geçmeli? Tek sütunlu index mi, yoksa composite index mi? Bu sorular sadece sözdizimsel tercihler değildir; veri tabanının disk okumalarını, RAM kullanımını ve yazma maliyetlerini doğrudan belirler. Yanlış index seçimi, milisaniyelerle ölçülmesi gereken sorguların dakikalara uzamasına yol açar. Doğru seçim ise hem okuma hem yazma trafiğini dengeli tutar.

Index Neden Sadece "Hızlandırıcı" Değildir?

Index, bir kitabın arka kapağındaki dizine benzetilse de bu benzetme yanıltıcı olabilir. Çünkü index, okuma performansını artırırken yazma operasyonlarına maliyet ekler. INSERT, UPDATE ve DELETE işlemleri her seferinde index ağacını güncellemek zorundadır. Bu yüzden index stratejisi, yalnızca "hangi sütun yavaş" sorusuna değil, "bu tablo ne sıklıkla yazılıyor, ne sıklıkla okunuyor" sorusuna da cevap vermelidir. detaylı kılavuzu konuya derinlemesine bir bakış sağlar.

Bir OLTP sistemi ile bir veri ambarı arasındaki fark da burada başlar. OLTP tarafında dar ve seçici indexler tercih edilirken, analitik yüklerde bitmap veya covering index daha mantıklı olabilir. Sorgu profilini anlamadan index eklemek, çoğu zaman performansı düşüren bir paradoksa dönüşür.

B-tree Index: Genel Amaçlı Standart

B-tree, ilişkisel veri tabanlarının varsayılan index tipidir. Dengeli ağaç yapısı sayesinde hem eşitlik (=) hem aralık (BETWEEN, <, >) sorgularında etkilidir. PostgreSQL, MySQL InnoDB, SQL Server ve Oracle, primary key ile birlikte otomatik olarak B-tree oluşturur.

B-tree'nin güçlü olduğu senaryolar:

  • Yüksek kardinaliteli sütunlar (örneğin user_id, email)
  • Aralık taramaları (created_at >= '2026-01-01')
  • ORDER BY ve LIMIT ile birlikte kullanılan sorgular
  • Birleşik (composite) yapılarda sol-prefix eşleşmesi

Buna karşın, çok düşük kardinaliteli sütunlarda (örneğin cinsiyet, durum bayrakları) B-tree'nin getirisi sınırlı kalır. Tarayıcı, ağaçtan çıkıp tabloyu doğrudan taramayı tercih edebilir.

Hash Index: Sadece Eşitlik İçin

Hash index, sütun değerinin hash'ini alıp bir bucket'a yazar. Bu nedenle yalnızca eşitlik karşılaştırmaları (=) için anlamlıdır. Aralık sorgularında, sıralama gerektiren işlemlerde veya LIKE 'abc%' kalıplarında işe yaramaz.

PostgreSQL'de hash index uzun süre WAL'a yazılmadığı için güvenilir bulunmuyordu; PostgreSQL 10 ile bu sorun çözüldü. MySQL Memory engine ve bazı in-memory veri tabanları (Redis, HANA) hash indexleri yoğun şekilde kullanır. Ortalama erişim O(1) olduğundan, doğru senaryoda B-tree'den hızlıdır.

Index seek ile index scan operasyonlarının veri tabanı sorgu maliyetlerini karşılaştıran iki panelli diyagram

Bitmap Index: Düşük Kardinalitenin Şampiyonu

Bitmap index, her benzersiz değer için bir bit dizisi tutar. Bu yapı, düşük kardinaliteli sütunlarda inanılmaz verimlidir. Cinsiyet, ülke kodu, durum bayrağı gibi alanlarda AND/OR operasyonları doğrudan bit düzeyinde yapılır, bu da CPU için son derece ucuzdur.

Ancak bitmap'in büyük dezavantajı vardır: yazma yoğun sistemlerde kilitleme maliyeti çok yüksektir. Tek bir satır güncellendiğinde ilgili bitmap segmentinin tamamı kilitlenir. Bu yüzden bitmap, OLTP sistemlerde değil; veri ambarı, BI ve raporlama yüklerinde tercih edilir. Oracle ve PostgreSQL'in BRIN/bitmap heap scan kombinasyonu bu alanın klasik örnekleridir.

Composite ve Covering Index Arasındaki İnce Çizgi

Composite index, birden fazla sütun üzerinde tek bir ağaç oluşturur. Sütun sırası kritiktir: (a, b, c) şeklinde tanımlı bir index, WHERE a=? sorgusunda kullanılır, WHERE b=? sorgusunda kullanılmaz. Bu kurala "sol prefix kuralı" denir.

Covering index ise sorgunun tüm SELECT sütunlarını index içinde barındırır. Böylece veri tabanı tabloya hiç dönmek zorunda kalmaz; bu işleme "index-only scan" denir. PostgreSQL'de INCLUDE klozu, SQL Server'da ise CREATE INDEX ... INCLUDE sözdizimi bu amaçla kullanılır.

  1. Filtre sütunlarını başa koyun (en seçici olan ilk sırada)
  2. ORDER BY sütunlarını sıralı şekilde ekleyin
  3. SELECT içinde sık dönen ek sütunları INCLUDE ile dahil edin
  4. Sütun sayısını 5'i geçirmemeye çalışın — yazma maliyeti üstel artar

Partial Index: Sadece İhtiyacın Olan Kadar

Partial index, sadece belirli bir WHERE koşulunu karşılayan satırlar için kurulur. Örneğin bir e-ticaret sistemindeki orders tablosunda yalnızca status = 'pending' satırlarına index koymak, hem disk hem bellek maliyetini düşürür. Aktif sipariş sayısı toplam siparişlerin %2'siyse, partial index B-tree'nin elli katı daha verimli olabilir.

PostgreSQL ve SQL Server (filtered index olarak) bu özelliği destekler; MySQL'de doğrudan eşdeğeri yoktur ancak generated column ile benzetilebilir. Partial indexler özellikle log tabloları, soft-delete işaretleri ve durum makineleri için biçilmiş kaftandır.

Seçim Matrisi: Hangi Senaryoda Hangi Index?

Aşağıdaki kabaca matris, günlük tasarım kararlarında pratik bir başlangıç noktası sunar:

  • Yüksek kardinalite + aralık sorgusu: B-tree
  • Yüksek kardinalite + sadece eşitlik + in-memory: Hash
  • Düşük kardinalite + read-heavy + analitik: Bitmap
  • Çok sütunlu filtre/sıralama: Composite (sol prefix)
  • Tabloya dönmeden okuma: Covering (INCLUDE)
  • Tablonun küçük bir alt kümesi: Partial
  • Çok büyük, doğal sıralı tablolar (zaman serileri): BRIN
  • JSON, dizi, full-text: GIN/GiST

Bu matrisin pratiğe dökülmesi için sorgu planlarını okumayı bilmek şarttır. EXPLAIN ANALYZE çıktısı üzerinden gerçek maliyetleri inceleme ve doğru index'i seçme yöntemlerini derinlemesine öğrenmek için SQL Performance ve Query Tuning eğitiminden yararlanabilirsiniz.

Ölçmeden Index Eklemeyin

Bir index'in işe yarayıp yaramadığını anlamanın tek yolu, sorgu planını analiz etmektir. PostgreSQL'de EXPLAIN (ANALYZE, BUFFERS), SQL Server'da SET STATISTICS IO ON, MySQL'de EXPLAIN FORMAT=JSON komutları kritik metrikleri verir. Index hit ratio, buffer reads, sequential scan oranı gibi göstergeler izlenmeden eklenen her index, çözümden çok problem üretebilir.

B-tree composite ve covering index türlerini farklı sorgu senaryolarında karşılaştıran matris diyagramı

Ayrıca kullanılmayan indexlerin tespiti için pg_stat_user_indexes, sys.dm_db_index_usage_stats ve performance_schema gibi sistem görünümleri düzenli olarak incelenmelidir. Aylarca dokunulmayan bir index, yazma maliyeti üreten ölü ağırlıktır.

Yazma Yükünü Unutmayın

Index sayısı arttıkça INSERT/UPDATE maliyeti doğrusal değil, çoğunlukla üstel biçimde artar. Çünkü her index kendi ağacını yeniden dengelemek zorundadır. Bir tabloda 10 index varsa, tek bir INSERT 10 ayrı ağacı günceller. Bu maliyet özellikle batch yüklemelerde, ETL pencerelerinde ve replikasyon gecikmelerinde kendini gösterir.

Bu yüzden büyük yüklemeler öncesinde indexleri DROP edip yükleme sonrası tekrar CREATE etmek, klasik bir performans tekniğidir. Modern veri tabanlarında CONCURRENTLY ve ONLINE seçenekleri ile bu süreç üretim ortamını kesintiye uğratmadan yönetilebilir. Index stratejisi, sadece okuma değil, tüm yaşam döngüsünü kapsayan bir tasarım kararıdır; bu konuyu pratiğe taşımak için sorgu optimizasyonu kaynaklarını inceleyebilirsiniz.