Teknoloji, Bilim ve Akıl
SQL Index Nedir? Veritabanı Sorgularını Hızlandırmanın Yolu
Veritabanı

SQL Index Nedir? Veritabanı Sorgularını Hızlandırmanın Yolu

6 dk okuma 1 okunma 0 yorum
Kısaca: SQL index, veritabanının tablo satırlarını baştan sona taramadan aranan kayda hızla ulaşmasını sağlayan sıralı bir arama yapısıdır. Doğru sütunlara eklendiğinde okuma sorgularının süresi belirgin biçimde kısalır.
İçindekiler
  1. SQL Index Neden Gereklidir?
  2. Index Nasıl Çalışır? B-Tree Yapısı Nedir?
  3. SQL Index Türleri Nelerdir?
  4. Index Nasıl Oluşturulur?
  5. Bileşik Index Nedir ve Sütun Sırası Neden Önemlidir?
  6. EXPLAIN ile Sorgunun Index Kullandığı Nasıl Anlaşılır?
  7. Hangi Sütunlara Index Eklenmelidir?
  8. Index Ne Zaman Zarar Verir?
  9. Index Varken Sorgu Neden Hâlâ Yavaş Olabilir?
  10. Sütun Üzerinde Fonksiyon Kullanmak
  11. Başında Joker Karakter Olan LIKE Araması
  12. Veri Tipi Uyuşmazlığı
  13. Sahadan Bir Örnek: Stok Hareket Raporu
  14. Özet: Index Kullanırken Kontrol Listesi

SQL index (dizin), bir tablodaki bir veya birkaç sütunun değerlerini sıralı biçimde tutan ve veritabanının aranan satıra tüm tabloyu okumadan ulaşmasını sağlayan ek bir veri yapısıdır. Kitabın sonundaki kavram dizini gibi çalışır. Aradığınız kelimenin hangi sayfada olduğunu dizinden bulur, doğrudan o sayfaya gidersiniz. Index yoksa veritabanı her sorguda tabloyu satır satır tarar. Buna tam tablo taraması (full table scan) denir.

Bu yazıda index yapısının nasıl çalıştığını, MySQL ve SQLite üzerinde nasıl oluşturulduğunu, hangi sütunlara eklenmesi gerektiğini ve EXPLAIN komutuyla sorgunun index kullanıp kullanmadığını nasıl kontrol edeceğinizi örneklerle anlatacağız.

SQL Index Neden Gereklidir?

Küçük tablolarda index olmaması fark edilmez. Birkaç yüz satırlık bir tabloyu taramak milisaniyeler sürer. Sorun, tablo büyüdükçe ortaya çıkar. Sipariş, log, stok hareketi veya ziyaret kaydı gibi sürekli büyüyen tablolarda her sorgu tüm satırları okumak zorunda kalırsa sayfa açılış süreleri uzar ve sunucu yükü artar.

Index ile veritabanı motoru aranan değeri sıralı yapı üzerinde ikili aramaya benzer bir yöntemle bulur. Okunması gereken satır sayısı tablonun tamamı yerine yalnızca eşleşen kayıtlarla sınırlı kalır.

  • WHERE koşullarında filtrelemeyi hızlandırır.
  • JOIN işlemlerinde tabloların eşleştirilmesini hızlandırır.
  • ORDER BY sıralamasında ayrı bir sıralama adımına gerek bırakmayabilir.
  • UNIQUE index ile yinelenen kayıt girilmesini veritabanı seviyesinde engeller.

Index Nasıl Çalışır? B-Tree Yapısı Nedir?

MySQL (InnoDB) ve SQLite dahil ilişkisel veritabanlarının çoğu varsayılan olarak B-Tree (dengeli ağaç) yapısını kullanır. B-Tree, değerleri sıralı tutan ve her düğümde birden fazla anahtar barındıran çok dallı bir ağaçtır. Ağaç dengeli kaldığı için kök düğümden yaprak düğüme giden yol kısadır ve milyonlarca satırda bile birkaç adımda hedefe ulaşılır.

Yaprak düğümlerde index değeri ile birlikte satırın tablodaki konumunu gösteren bir işaretçi bulunur. InnoDB tarafında bu işaretçi birincil anahtardır. SQLite tarafında ise rowid değeridir. Veritabanı önce index üzerinde arama yapar, ardından işaretçiyi kullanarak asıl satıra gider.

Değerler sıralı tutulduğu için B-Tree yalnızca eşitlik aramasında değil, aralık sorgularında da etkilidir. Tarih aralığı, fiyat aralığı veya belirli bir harfle başlayan metin aramaları bu yüzden index ile hızlanır.

SQL Index Türleri Nelerdir?

Index TürüNe İşe Yarar?Örnek Kullanım
Primary KeyHer satırı benzersiz tanımlar, otomatik olarak index oluştururid sütunu
Unique IndexAynı değerin iki kez girilmesini engeller ve aramayı hızlandırıre-posta, TC kimlik, barkod
Normal (Tekli) IndexTek sütun üzerinde arama ve sıralamayı hızlandırırsipariş tarihi, kategori
Bileşik (Composite) IndexBirden fazla sütunu tek index içinde birlikte tutarmüşteri ve tarih birlikte
Full-Text IndexUzun metinlerde kelime bazlı arama yaparmakale içeriği araması

Foreign key tanımlandığında MySQL InnoDB, ilgili sütunda index yoksa otomatik olarak oluşturur. SQLite ise foreign key sütunları için otomatik index oluşturmaz. Bu fark, SQLite kullanılan projelerde JOIN sorgularının beklenmedik şekilde yavaş çalışmasının yaygın sebeplerinden biridir.

Index Nasıl Oluşturulur?

Index oluşturma komutu MySQL ve SQLite tarafında neredeyse aynıdır. Örnek olarak bir sipariş tablosu üzerinden ilerleyelim.

CREATE TABLE siparisler (
    id INTEGER PRIMARY KEY,
    musteri_id INTEGER NOT NULL,
    durum VARCHAR(20) NOT NULL,
    tutar DECIMAL(10,2) NOT NULL,
    siparis_tarihi DATETIME NOT NULL
);

-- Tekli index
CREATE INDEX idx_siparis_tarih ON siparisler (siparis_tarihi);

-- Bileşik index
CREATE INDEX idx_siparis_musteri_tarih ON siparisler (musteri_id, siparis_tarihi);

-- Benzersiz index
CREATE UNIQUE INDEX ux_kullanici_eposta ON kullanicilar (eposta);

Index silmek için kullanılan sözdizimi iki veritabanında farklıdır:

-- MySQL
DROP INDEX idx_siparis_tarih ON siparisler;

-- SQLite
DROP INDEX idx_siparis_tarih;

Index adlandırmada tutarlı bir kural kullanmak bakımı kolaylaştırır. idx_tablo_sutun normal index için, ux_tablo_sutun benzersiz index için pratik bir düzendir. Aylar sonra şemaya baktığınızda hangi indexin ne işe yaradığını adından anlarsınız.

Bileşik Index Nedir ve Sütun Sırası Neden Önemlidir?

Bileşik index, birden fazla sütunu soldan sağa sıralı şekilde tek bir yapıda tutar. Telefon rehberindeki soyad ve ad sıralamasına benzer. Rehber önce soyada, soyadı aynı olanlar kendi içinde ada göre sıralanır.

Bu yapının önemli bir sonucu vardır: en soldaki sütun kuralı. (musteri_id, siparis_tarihi) indexi şu sorgularda kullanılır:

  • WHERE musteri_id = 42
  • WHERE musteri_id = 42 AND siparis_tarihi >= '2026-01-01'
  • WHERE musteri_id = 42 ORDER BY siparis_tarihi DESC

Ancak yalnızca WHERE siparis_tarihi >= '2026-01-01' koşulu bu indexi verimli kullanamaz, çünkü indexin ilk sütunu olan müşteri numarası sorguda yoktur. Rehberde soyadını bilmeden yalnızca ada göre arama yapmaya benzer.

Pratik kural şudur: eşitlik (=) ile filtrelenen sütunlar öne, aralık (>, <, BETWEEN) ile filtrelenen veya sıralamada kullanılan sütunlar arkaya yazılır.

EXPLAIN ile Sorgunun Index Kullandığı Nasıl Anlaşılır?

Bir sorgunun index kullanıp kullanmadığını tahmin etmek yerine ölçmek gerekir. Bunun için sorgunun başına EXPLAIN eklenir.

-- MySQL
EXPLAIN SELECT * FROM siparisler
WHERE musteri_id = 42 AND siparis_tarihi >= '2026-01-01';

-- SQLite
EXPLAIN QUERY PLAN SELECT * FROM siparisler
WHERE musteri_id = 42 AND siparis_tarihi >= '2026-01-01';

MySQL çıktısında dikkat edilmesi gereken sütunlar şunlardır:

SütunAnlamıİyi mi Kötü mü?
type = ALLTüm tablo taranıyorBüyük tabloda kötü
type = ref veya rangeIndex ile eşitlik veya aralık araması yapılıyorİyi
type = constPrimary key veya unique ile tek satır bulunuyorEn iyi
keyKullanılan indexin adıBoş ise index kullanılmıyor
rowsOkunacağı tahmin edilen satır sayısıNe kadar düşükse o kadar iyi

SQLite tarafında çıktı daha sadedir. SCAN siparisler ifadesi tam tablo taraması anlamına gelir. SEARCH siparisler USING INDEX idx_siparis_musteri_tarih ifadesi ise indexin kullanıldığını gösterir.

Hangi Sütunlara Index Eklenmelidir?

Her sütuna index eklemek çözüm değildir. Index eklenecek sütunları belirlemek için uygulamanın gerçekten çalıştırdığı sorgulara bakmak gerekir.

  • WHERE koşulunda sık kullanılan sütunlar: durum, kategori, kullanıcı numarası gibi.
  • JOIN ile bağlanan sütunlar: özellikle foreign key sütunları.
  • ORDER BY ile sıralanan sütunlar: listelerde tarihe göre sıralama gibi.
  • Benzersiz olması gereken sütunlar: e-posta, kullanıcı adı, barkod.

Seçiciliği düşük sütunlarda index genellikle işe yaramaz. Yalnızca iki farklı değer alan aktif veya pasif gibi bir sütunda veritabanı motoru çoğu zaman indexi atlayıp tabloyu taramayı tercih eder. Bu tür sütunlar tek başına değil, bileşik indexin parçası olarak daha anlamlıdır.

Index Ne Zaman Zarar Verir?

Index okuma işlemlerini hızlandırırken yazma işlemlerine ek yük getirir. Her INSERT, UPDATE ve DELETE işleminde veritabanı ilgili tüm indexleri de güncellemek zorundadır.

  • Çok sayıda index, toplu veri aktarımlarını yavaşlatır.
  • Her index diskte ek alan kaplar.
  • Birbirini kapsayan indexler gereksiz yük oluşturur. (musteri_id, siparis_tarihi) varken ayrıca yalnızca (musteri_id) indexi tutmaya genellikle gerek yoktur.

Toplu veri aktarımı yapılacaksa pratik bir yöntem, işlemi tek transaction içinde yapmak ve gerekiyorsa aktarım sonrasında indexleri oluşturmaktır.

Index Varken Sorgu Neden Hâlâ Yavaş Olabilir?

Index tanımlı olduğu halde sorgunun onu kullanmamasının birkaç yaygın sebebi vardır.

Sütun Üzerinde Fonksiyon Kullanmak

Aşağıdaki sorgu, sipariş tarihi üzerinde index olsa bile tüm tabloyu tarar. Çünkü veritabanı her satırda fonksiyonu çalıştırıp sonucu karşılaştırmak zorundadır.

-- Index kullanılamaz
SELECT * FROM siparisler WHERE YEAR(siparis_tarihi) = 2026;

-- Index kullanılır
SELECT * FROM siparisler
WHERE siparis_tarihi >= '2026-01-01' AND siparis_tarihi < '2027-01-01';

Başında Joker Karakter Olan LIKE Araması

LIKE 'kalem%' araması index kullanabilir, çünkü sıralı yapıda başlangıç noktası bellidir. LIKE '%kalem%' ise index kullanamaz. İçerik içinde arama gerekiyorsa MySQL tarafında FULLTEXT, SQLite tarafında FTS5 sanal tabloları daha doğru çözümdür.

Veri Tipi Uyuşmazlığı

Metin tipinde tutulan bir telefon veya barkod sütununu sayı ile karşılaştırmak, bazı durumlarda örtük tip dönüşümüne ve indexin devre dışı kalmasına yol açar. Karşılaştırmayı her zaman sütunun kendi tipinde yapmak gerekir.

Sahadan Bir Örnek: Stok Hareket Raporu

Fabrikada kullandığımız stok takip uygulamasında hareket tablosu ilk aylarda sorunsuz çalışıyordu. Kayıt sayısı büyüdükçe belirli bir ürün kodu için tarih aralığı raporu belirgin şekilde yavaşlamaya başladı. EXPLAIN çıktısında sorgunun tüm tabloyu taradığı açıkça görünüyordu.

Sorgu hem ürün koduna eşitlikle hem de tarihe aralıkla filtreleme yapıyordu. Bu yüzden (urun_kodu, hareket_tarihi) sırasıyla bileşik index eklendi. Index sonrasında EXPLAIN çıktısı tam tarama yerine index araması gösterdi ve rapor ekranı kullanıcıyı bekletmeden açılır hale geldi. Aynı projede önceden eklenmiş ama hiçbir sorguda kullanılmayan iki index de kaldırıldı ve günlük toplu veri aktarımı hızlandı.

Bu deneyimden çıkan ders şudur: index eklemeden önce yavaş sorguyu tespit etmek, EXPLAIN ile doğrulamak ve index sonrasında tekrar ölçmek gerekir. Tahmine dayalı index eklemek çoğu zaman gereksiz yük getirir.

Özet: Index Kullanırken Kontrol Listesi

  1. Yavaş çalışan sorguyu belirleyin.
  2. EXPLAIN ile tam tablo taraması olup olmadığına bakın.
  3. WHERE, JOIN ve ORDER BY içinde kullanılan sütunları listeleyin.
  4. Eşitlik sütunları önde olacak şekilde bileşik index tasarlayın.
  5. Sütun üzerinde fonksiyon kullanan koşulları aralık sorgusuna çevirin.
  6. Index sonrası EXPLAIN çıktısını tekrar kontrol edin.
  7. Kullanılmayan ve birbirini kapsayan indexleri temizleyin.

Sık Sorulan Sorular

Primary key için ayrıca index oluşturmak gerekir mi?
Hayır. Primary key tanımlandığında veritabanı bu sütun için otomatik olarak benzersiz bir index oluşturur. Aynı sütuna ikinci bir index eklemek yalnızca gereksiz yük getirir.
Bir tabloya en fazla kaç index eklenmelidir?
Kesin bir sayı yoktur. Belirleyici olan, tablonun okuma ve yazma oranıdır. Sürekli veri yazılan log tablolarında index sayısını az tutmak, çoğunlukla okunan rapor tablolarında ise sorgulara göre daha fazla index kullanmak mantıklıdır.
SQLite veritabanında index kullanmak gerekli mi?
Evet. SQLite küçük projelerde kullanılsa da tablo büyüdükçe index ihtiyacı diğer veritabanlarıyla aynıdır. Özellikle SQLite foreign key sütunlarına otomatik index eklemediği için bu sütunlara elle index tanımlamak önemlidir.
Index eklemek mevcut verileri etkiler mi?
Hayır, index tablodaki verileri değiştirmez, yalnızca ek bir arama yapısı oluşturur. Ancak büyük tablolarda index oluşturma işlemi zaman alabilir ve bazı veritabanlarında bu süre boyunca tabloya yazma işlemleri yavaşlayabilir.
EXPLAIN çıktısında index görünüyor ama sorgu hâlâ yavaş, neden?
Index kullanılsa bile çok fazla satır eşleşiyorsa veya SELECT ile gereksiz sütunlar çekiliyorsa sorgu yavaş kalabilir. Yalnızca gereken sütunları seçmek, LIMIT kullanmak ve gerekirse sorguyu kapsayan bir bileşik index tasarlamak çözüm olur.
Ramazan Yeşildal
Ramazan Yeşildal

Karaman merkezli bilgi teknolojileri yöneticisi ve bağımsız yazılım geliştirici.

0 Yorum

Yorum yazın

E-posta adresiniz yayınlanmaz. Yorumlar onaydan sonra görünür.