Menu

SQLite Composite Index: Sütun Sırası ve Soldan Önek Kuralı

SQLite'ta çok sütunlu indekslerin nasıl çalıştığını, sütun sırasının neden bu kadar önemli olduğunu ve composite index'in ne zaman işe yaradığını, ne zaman yer kaplayan ölü ağırlığa dönüştüğünü inceliyoruz.

Bu sayfada çalıştırılabilir editörler var — düzenle, çalıştır ve sonucu anında gör.

Tek Index, Birden Fazla Sütun

Composite index — yani çok sütunlu index — iki ya da daha fazla sütun üzerine kurulan tek bir index'tir. Oluştururken sütunları sırayla yazmanız yeterli:

idx_orders_customer_status indexi, kayıtları önce customer_id'ye, ardından her müşteri içinde status'a göre sıralı tutar. Bu sıralama meselenin tamamı — composite index'lerle ilgili geri kalan her şey buradan türüyor.

Zihinsel Model: Sıralı Bir Telefon Rehberi

Eski tarz bir telefon rehberi düşün. Kayıtlar önce soyada göre, aynı soyad içinde ise ada göre sıralı. (last_name, first_name) üzerindeki bir index aynen böyle görünür.

Bazı sorgular ucuza gelir, bazıları gelmez:

  • "Soyadı Patel olan herkesi bul" — kolay, tüm Patel'ler yan yana.
  • "Priya Patel'i bul" — kolay, Patel'e atla, sonra Priya'ya kadar tara.
  • "Adı Priya olan herkesi bul" — yavaş, her sayfayı baştan sona taramanız gerekir. Priya'lar her soyadın altına dağılmış durumda.

SQLite'taki çok sütunlu bir index de tam olarak böyle çalışır. İlk sütun birincil sıralama anahtarıdır; ikinci sütun yalnızca ilk sütun değeri aynı olan kayıtları kendi içinde sıralar.

Leftmost Prefix Kuralı

SQLite, bir composite index'i ancak WHERE koşulu o index'in sütunlarının en soldaki bir önekini (leftmost prefix) kısıtlıyorsa kullanabilir. (a, b, c) üzerindeki bir index için:

  • Sadece a ile filtreleme — index kullanılır.
  • a ve b ile filtreleme — index kullanılır.
  • a, b ve c ile filtreleme — index kullanılır.
  • Tek başına b, tek başına c ya da b ve c ile filtreleme — index kullanılmaz.

Bunu doğrudan EXPLAIN QUERY PLAN ile doğrulayabilirsiniz:

İlk planda SEARCH events USING INDEX idx_events_user_kind_time görüyoruz. İkincisi ise SCAN events'e düşüyor — sadece kind üzerinden filtrelemek baştaki user_id sütununu atladığı için index bu sorguda işe yaramıyor.

Sütun Sırası Bir Tasarım Kararıdır

Leftmost prefix kuralı devreye girdiğinden, CREATE INDEX içinde sütunları hangi sırayla yazdığınız stilistik bir tercih değil, gerçek bir karardır. İki pratik kural:

  1. En sık filtrelediğiniz sütunu başa koyun. O sütun, index'i en geniş sorgu yelpazesine açar.
  2. Eşitlik (equality) sütunlarını aralık (range) sütunlarından önce yazın. SQLite önce = ile index'in derinine inip ardından <, > veya BETWEEN ile bitişik bir aralığı tarayabilir — ama bunu yalnızca kullanılan son sütunda yapabilir.

Plan çıktısında şunu görüyorsunuz: SEARCH sales USING INDEX idx_sales_region_time (region=? AND sold_at>?). SQLite doğrudan region = 'EU' satırlarına atlıyor, ardından tarih aralığı boyunca ileri doğru ilerliyor. Sütun sırasını (sold_at, region) şeklinde değiştirdiğinde ise aynı sorgu, tarih aralığındaki tüm satırları taramak ve her biri için region değerini tekrar kontrol etmek zorunda kalıyor.

Composite Index mi, Birden Fazla Tek Sütunlu Index mi?

Sıkça karşılaşılan bir soru: (a, b) üzerinde tek bir index mi oluşturmalıyım, yoksa a ve b için ayrı ayrı iki index mi?

Birleşik filtrede composite index açık ara daha hızlıdır — SQLite eşleşen (project_id, state) kayıtlarına doğrudan gider. İki ayrı tek sütunlu index olduğunda ise SQLite genelde birini seçip satırları daraltır, sonra eşleşen her satırda diğer sütunu yeniden kontrol eder. Bazen iki index'i kesişim yoluyla kullanabilir, ancak iki sütun birlikte sorgulandığında çok sütunlu index (composite index) çok daha temiz bir çözümdür.

project_id ve state ayrı ayrı da sorgulanıyorsa ikisini birden tutmak isteyebilirsiniz: birleşik filtre için composite index, sadece state üzerinden filtre uygulayan sorgular için de tek sütunlu bir index.

Covering Index (Kapsayıcı Index)

Bir index, sorgunun ihtiyaç duyduğu tüm sütunları — hem filtre sütunlarını hem de seçilen sütunları — içeriyorsa SQLite yanıtı tabloya hiç dokunmadan üretebilir. Buna covering index denir ve bir sorgunun ulaşabileceği en yüksek hız budur.

Planda USING COVERING INDEX idx_invoices_cover ifadesini görüyorsunuz. Sorgu issued_at ve total değerlerini doğrudan index'ten okuyor; notes ve id gerekmediği için tabloya hiç dokunulmuyor. Sürekli çalışan bir sorguyu komple karşılayabilmek adına composite index'e fazladan bir sütun eklemek, bu tarz durumlarda kesinlikle değer.

Composite UNIQUE Kısıtlamaları

SQLite composite index'leri, sütun kombinasyonları üzerinde benzersizliği zorlamak için de kullanılır. Tek başına hiçbir sütunun unique olmadığı, ama kombinasyonun unique olması gereken senaryolarda işe yarar:

Üçüncü insert şu hatayı fırlatır: UNIQUE constraint failed: enrollments.student_id, enrollments.course_id. Aynı çift zaten index'te mevcut olduğu için SQLite mükerrer kaydı reddeder.

Dikkat Edilmesi Gereken Tuzaklar

  • Baştaki olmayan sütunlar arasındaki OR index'i devre dışı bırakır. (a, b) index'i üzerinde WHERE a = 1 OR b = 2 sorgusu genellikle index'ten hiç faydalanamaz — SQLite iki dalı ayrı ayrı değerlendirmek zorunda kalır.
  • Index'lenmiş sütunlar üzerindeki fonksiyonlar index'i etkisiz kılar. WHERE lower(email) = 'x' ifadesi email sütunundaki index'i kullanmaz. Bunun yerine ifadenin kendisini index'leyin ya da veriyi insert sırasında normalize edin.
  • Index'ler bedava değildir. Her INSERT, (index'li sütunları etkileyen) UPDATE ve DELETE işleminde tüm index'ler güncellenir. Yazma yükü ağır bir tabloda üç tane composite index, yazma maliyetinin büyük kısmını yutabilir.
  • Index'leri oluşturduktan sonra mutlaka ANALYZE çalıştırın. SQLite'ın sorgu planlayıcısı, aday index'ler arasında seçim yaparken ANALYZE ile toplanan istatistiklere bakar. Bu istatistikler yoksa, her zaman optimal olmayan sezgisel kurallara geri döner.

Pratik Bir İş Akışı

Yavaş bir sorguyu iyileştirirken döngü genellikle şöyle ilerler:

  1. Sorgu üzerinde EXPLAIN QUERY PLAN çalıştırın ve SQLite'ın şu an ne yaptığını görün.

  2. Tablo taraması yapıyorsa WHERE cümlesine bakın — eşitlik sütunu hangisi? Aralık (range) sütunu hangisi? Hangi sütunlar seçiliyor?

  3. Önce eşitlik, sonra aralık olacak şekilde sıralanmış bir composite index kurun; covering index işe yarayacaksa seçilen sütunları da sona ekleyin.

  4. ANALYZE çalıştırın.

  5. EXPLAIN QUERY PLAN'ı tekrar çalıştırın. Planın değiştiğini ve index'in kullanıldığını doğrulayın.

  6. Sorguyu, gerçeği temsil eden veriler üzerinde önce ve sonra olarak ölçün.

  7. adımı atlarsanız sonuçlarına katlanırsınız. Planda doğru görünen bir index, tablo küçükse ya da planlayıcı farklı bir yol seçerse pratikte daha yavaş kalabilir.

Sırada: Partial Index'ler

Composite index'ler tablodaki her satırı kapsar. Ama çoğu zaman yalnızca küçük bir satır kümesi önemlidir — açık biletler, işlenmemiş işler, silinmemiş kayıtlar. Partial index, index'in kendisine gömülü bir WHERE cümlesiyle yalnızca o satırları index'lemenizi sağlar. Bir sonraki sayfanın konusu bu.

Sıkça Sorulan Sorular

SQLite'ta composite index nedir?

Composite index, iki ya da daha fazla sütunu birden kapsayan tek bir indekstir. CREATE INDEX idx_name ON table(col_a, col_b) şeklinde oluşturursunuz. SQLite kayıtları önce col_a'ya göre, sonra her col_a değeri içinde col_b'ye göre sıralı tutar — tıpkı önce soyada, ardından ada göre sıralanmış bir telefon rehberi gibi.

SQLite composite index'te sütun sırası önemli mi?

Hem de çok. SQLite bir composite index'i ancak WHERE koşulu indekslenmiş sütunların soldan başlayan bir önekini (leftmost prefix) filtreliyorsa kullanabilir. (a, b, c) üzerindeki bir indeks; sadece a, a ve b, ya da üçünün birden filtrelendiği sorgulara yardım eder — ama yalnızca b veya yalnızca c üzerinden filtreleyen bir sorguya hiçbir faydası olmaz.

Tek sütunlu indeksler yerine ne zaman composite index kullanmalıyım?

Sorgularınız sürekli aynı sütun kombinasyonu üzerinden filtreleme veya sıralama yapıyorsa composite index mantıklıdır. Sütunlar bağımsız olarak sorgulanıyorsa ayrı ayrı tek sütunlu indeksler daha uygundur. SQLite'ın hangi indeksi gerçekten seçtiğini görmek için EXPLAIN QUERY PLAN çalıştırın — güvenebileceğiniz tek geri bildirim budur.

SQLite'ta covering index nedir?

Covering index, sorgunun ihtiyaç duyduğu tüm sütunları içeren bir indekstir; böylece SQLite cevabı doğrudan indeksten döndürür, tabloya hiç dokunmaz. Bu durumda EXPLAIN QUERY PLAN çıktısında USING COVERING INDEX ifadesini görürsünüz. Sık çalışan bir sorguyu hızlandırmak için composite index'e fazladan sütun eklemek yaygın bir optimizasyon yöntemidir.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA