Index Aslında Ne İşe Yarar?
Index, ayrı bir veri yapısıdır — sıralı bir B-tree — ve SQLite'ın bir sütunun değerine göre satırları, tüm tabloyu taramadan bulmasını sağlar. Index yokken WHERE email = 'rosa@example.com' gibi bir sorgu her satırı okur ve tek tek kontrol eder. email üzerinde bir index varsa, SQLite ağaçta yaklaşık log(n) adımda dolaşıp eşleşmeye doğrudan atlar.
Tabii bu hız bedava değil. Index, indexlenen sütunun bir kopyası artı ilgili satıra giden bir işaretçidir. Her INSERT, indexlenen bir sütunun her UPDATE'i ve her DELETE işlemi index'i de güncellemek zorundadır. Disk kullanımı artar, yazma performansı bir miktar düşer. Anlaşma şu: yazarken biraz öde, okurken çok daha fazlasını kazan.
SQLite Create Index ile Index Oluşturma
Temel söz dizimi şöyle:
İsimlendirme kuralı: çoğu ekip idx_<tablo>_<kolon> kalıbını kullanır; böylece index'in ne işe yaradığı bir bakışta anlaşılır. İsim, sadece tablo içinde değil tüm veritabanında benzersiz olmak zorunda — tablo adının isme dahil edilmesinin sebebi de bu.
Bir index'i kaldırmak için:
DROP INDEX idx_users_email;
Index'ler tamamen performans iskelesidir. Bir index'i silmek verilerinize asla dokunmaz — sadece sorguların ne kadar hızlı çalıştığını etkiler.
Unique Index Kullanımı
Unique index iki işi birden yapar: hem aramaları hızlandırır hem de aynı değere sahip iki satırın tabloya girmesini engeller.
Üçüncü INSERT UNIQUE constraint failed: accounts.username hatasıyla düşüyor. SQLite, PRIMARY KEY ve UNIQUE sütunları için otomatik olarak unique index oluşturur — bunları sqlite_autoindex_<tablo>_<n> şeklinde isimlendirilmiş görürsünüz. CREATE UNIQUE INDEX yazmaya yalnızca kısıtlama tablonun kendisinde tanımlanmadığında ihtiyaç duyarsınız.
Query planner aslında ne yapar?
Index eklemek, SQLite'ın onu kullanacağını garanti etmez. Query planner her sorgu için ayrı bir strateji seçer ve seçtiği planı EXPLAIN QUERY PLAN ile görebilirsiniz:
Çıktıda SEARCH ... USING INDEX idx_orders_customer ifadesini arayın — bu, index'in kullanıldığı anlamına gelir. Eğer SCAN orders görüyorsanız, ya planlayıcı tam tablo taramasının daha ucuz olduğuna karar vermiştir (küçük tablolarda çoğu zaman doğru tercihtir) ya da sorgunuzun yapısı index'in kullanılmasını engellemiştir. Bu planları okumayla ilgili ayrı bir döküman ileride sizi bekliyor.
SQLite Index Ne Zaman Kullanılmaz?
Index'lerin bilinen birkaç kör noktası vardır. Aşağıdaki durumların her biri, email üzerindeki index'i devre dışı bırakır:
-- Fonksiyon sütunu sarmalıyor
SELECT * FROM users WHERE lower(email) = 'rosa@example.com';
-- LIKE içinde başta joker karakter
SELECT * FROM users WHERE email LIKE '%@example.com';
-- Tür uyuşmazlığı dönüşüme zorluyor
SELECT * FROM users WHERE email = 12345;
B-tree, ham email değerine göre sıralı tutulur; dolayısıyla sorgu sırasında kolonu dönüştüren her işlem tam tarama yapmaya zorlar. Çözüm yolları farklı olabilir: veriyi zaten normalize edilmiş şekilde saklayın (email_lower kolonu), bir ifade indeksi kullanın (CREATE INDEX idx ON users(lower(email))) ya da alt dize araması için SQLite'ın tam metin aramasından (FTS) yararlanın.
Kapsayan İndeksler (Covering Index)
Bir indeks, sorgunun ihtiyaç duyduğu tüm kolonları zaten içeriyorsa SQLite tabloya hiç dokunmadan sorguyu yanıtlayabilir — işte buna covering index denir. Püf nokta, indeks tanımına ek kolonları da dahil etmektir:
Sorgunun istediği iki sütun da indeksin içinde durduğu için SQLite USING INDEX çıktısını veriyor. Yani satıra hiç gitmesine gerek kalmıyor. Covering index'ler, sık çalışan okuma yollarında elde edebileceğin en yüksek kazançlı optimizasyonlardan biri — karşılığında indeks biraz şişiyor tabii. Çok sütunlu indeksler başlı başına bir konu; bir sonraki yazı onları detaylıca anlatıyor.
SQLite'ta Index Listeleme ve İnceleme
Mevcut indeksleri görmenin iki yolu var:
Bu komut, veritabanındaki her indeksi CREATE ifadesiyle birlikte listeler. Tek bir tabloya odaklanmak istiyorsanız, PRAGMA index_list('products'); size sadece o tablonun indekslerini verir; PRAGMA index_info('idx_products_name'); ise her indeksin hangi sütunları kapsadığını gösterir. sqlite_autoindex_ ile başlayan her şey, bir PRIMARY KEY ya da UNIQUE kısıtı için otomatik oluşturulmuştur — bunları silemezsiniz.
SQLite Index Ne Zaman Kullanılmaz?
İndeks eklemenin işleri kötüleştirdiği birkaç durum var:
- Küçük tablolar. Birkaç yüz satır mikrosaniyeler içinde taranır. Sorgu planlayıcı zaten indeksi muhtemelen görmezden gelir; siz de boşuna yazma maliyeti eklemiş olursunuz.
- Yazma yoğun ama nadiren sorgulanan sütunlar. Her yazma işlemi her indeksi günceller. Neredeyse hiç filtrelemediğiniz bir sütunu indekslemek, saf maliyetten ibarettir.
- Tek başına düşük kardinaliteli sütunlar. Yalnızca üç değer alabilen bir
statussütununa konulan indeks, sonucu pek daraltmaz. Bileşik bir indeksin ikinci sütunu ya da partial index olarak hâlâ işe yarayabilir — ama tek başına genelde zahmete değmez. - Zaten kapsanan durumlar.
(a, b)üzerinde bir indeksiniz varsa, ayrıca(a)için bir indekse ihtiyacınız yok. SQLite, yalnızcaaile filtreleyen sorgularda da bileşik indeksin baştaki sütunlarını kullanır.
"Bu indeksi eklemeli miyim?" sorusunun dürüst cevabı neredeyse her zaman aynı: deneyin, EXPLAIN QUERY PLAN çalıştırın, gerçekçi verilerle ölçün ve ona göre karar verin.
Sırada: Bileşik İndeksler
Tek sütunlu bir indeks epey iş görür, ama gerçek sorgular çoğu zaman birden fazla sütunla filtreler ve sıralar. İşte bileşik indeksler — yani (a, b, c) üzerine kurulan indeksler — tam olarak bu işi yapar ve sütunların sırası, çoğu kişinin sandığından çok daha önemlidir. Bir sonraki sayfanın konusu bu.
Sıkça Sorulan Sorular
SQLite'ta index nasıl oluşturulur?
Temel söz dizimi şöyle: CREATE INDEX index_adi ON tablo_adi(kolon_adi);. Tekillik istiyorsanız CREATE UNIQUE INDEX kullanın. Dikkat: index adının sadece tablo içinde değil, tüm veritabanında benzersiz olması gerekiyor. Silmek istediğinizde de DROP INDEX index_adi; yeterli.
SQLite'ta ne zaman index eklemeliyim?
Sık sık WHERE, JOIN ya da ORDER BY ile kullandığınız kolonlara index eklemek mantıklı — özellikle tablo büyükse ve sorgu satırların küçük bir kısmını döndürüyorsa. Ama her kolona index atmak hata: her index INSERT, UPDATE ve DELETE işlemlerini yavaşlatır, üstüne disk de yer. Eklemeden önce ve sonra mutlaka EXPLAIN QUERY PLAN ile planlayıcının index'i gerçekten kullandığından emin olun.
SQLite neden index'imi kullanmıyor?
En sık görülen sebepler şunlar: tablo o kadar küçük ki tam tarama zaten daha ucuz; kolon bir fonksiyona sarılmış (WHERE lower(email) = ... yazarsanız email üzerindeki index devreye girmez); sorguda index'siz kolonlar üzerinde OR var; ya da istatistikler eskimiş. Çözüm için ANALYZE çalıştırarak istatistikleri tazeleyin ve EXPLAIN QUERY PLAN ile planlayıcının ne seçtiğine bakın.
SQLite'ta bir tablonun tüm index'lerini nasıl listelerim?
Belirli bir tablo için PRAGMA index_list('tablo_adi'); çalıştırabilir ya da doğrudan sqlite_master üzerinden sorgulayabilirsiniz: SELECT name, sql FROM sqlite_master WHERE type = 'index';. Listede gördüğünüz sqlite_autoindex_* kayıtları, PRIMARY KEY ve UNIQUE kısıtları için SQLite'ın otomatik oluşturduğu index'lerdir.