SQL INDEX STRATEJİLERİ

Veri Akademi8 dk okuma
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.

1. 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.

2. 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.

3. 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

4. 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.

5. 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

6. 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.

7. 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.

8. Ö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.

9. 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.

Sıkça Sorulan Sorular

B-tree ve hash index arasında nasıl seçim yapmalıyım?

Sorgularınız yalnızca eşitlik karşılaştırmaları kullanıyorsa ve veri in-memory tutuluyorsa hash index daha hızlı olabilir. Ancak aralık sorguları, ORDER BY veya LIKE 'prefix%' kullanıyorsanız hash index işe yaramaz. Bu durumlarda B-tree neredeyse her zaman doğru tercihtir ve genel amaçlı standart olarak kabul edilir.

Bitmap index neden OLTP sistemlerde önerilmez?

Bitmap index, bir satır güncellendiğinde ilgili bitmap segmentinin tamamını kilitler. Yazma yoğun bir OLTP sisteminde bu durum ciddi concurrency sorunlarına yol açar. Bitmap, daha çok veri ambarı, BI raporlama ve düşük kardinaliteli analitik sorgular için biçilmiş kaftandır. Yazma maliyeti yüksek olduğundan canlı işlem tablolarında kaçınılması gereken bir yapıdır.

Composite index'te sütun sırası neden bu kadar önemli?

Composite index, sol prefix kuralı ile çalışır. Yani (a, b, c) şeklinde tanımlı bir index WHERE a=? veya WHERE a=? AND b=? sorgularında kullanılır, ama WHERE b=? sorgusunda kullanılmaz. En seçici sütunu başa koymak, indexin etkinliğini doğrudan etkiler ve gereksiz tarama maliyetini önler.

Covering index ile composite index aynı şey midir?

Hayır. Composite index, birden fazla sütun üzerinde sorgu filtrelemek için kurulur. Covering index ise sorgunun tüm SELECT sütunlarını index içinde barındırır ve tabloya dönmeden 'index-only scan' yapılmasını sağlar. PostgreSQL'de INCLUDE, SQL Server'da INCLUDE klozu bu amaçla kullanılır.

Partial index ne zaman tercih edilmeli?

Bir tablonun sadece küçük bir alt kümesi sorgulanıyorsa partial index idealdir. Örneğin milyonlarca sipariş içinden yalnızca 'pending' durumdaki birkaç bin kayıt aktif olarak filtreleniyorsa, partial index hem disk alanından tasarruf sağlar hem de okuma performansını ciddi şekilde artırır. Log tabloları ve soft-delete kullanımı için ideal bir çözümdür.

Kullanılmayan indexleri nasıl tespit ederim?

PostgreSQL'de pg_stat_user_indexes, SQL Server'da sys.dm_db_index_usage_stats, MySQL'de performance_schema sistem görünümleri kullanılır. Bu görünümler indexlerin son ne zaman okunduğunu ve kaç kez taranıp kaç kez seek edildiğini gösterir. Aylarca dokunulmamış indexler yazma maliyeti üreten ölü ağırlık olduğundan kaldırılmalıdır.

Index sayısı arttıkça performans neden düşer?

Her index kendi ağaç yapısını korumak zorundadır. Bir tabloda 10 index varsa, tek bir INSERT işlemi 10 ayrı ağacı günceller ve yeniden dengeler. Bu maliyet özellikle batch yüklemelerde, ETL pencerelerinde ve replikasyon süreçlerinde belirginleşir. Bu yüzden index sayısı sorgu profili ile dengeli tutulmalıdır.

EXPLAIN ANALYZE çıktısında neye dikkat etmeliyim?

Sequential scan yerine index scan veya index-only scan görmek istersiniz. Buffer reads sayısı, gerçek satır sayısı ile tahmini satır sayısı arasındaki sapma ve toplam yürütme süresi kritik metriklerdir. Ayrıca Rows Removed by Filter yüksekse index seçici çalışmıyor demektir ve sütun sırası gözden geçirilmelidir.