Database Indexing Nedir? Yavaş Sorguları Anında Hızlandırmanın Bilimi
10 milyon satırlık bir tabloda WHERE email = "ali@example.com" sorgusu çalıştırıyorsunuz. Index yoksa veritabanı her satırı tek tek kontrol eder — tam table scan. Index varsa direkt sonuca atlar.
Fark? Saniyeler ile milisaniyeler.
Index Neden Gerekli?
Veritabanı tablolarını düşünün: veriler disk üzerinde sırasız saklanır. Bir kaydı bulmak için veritabanının tüm satırları taraması gerekir — O(n) kompleksite. 10 milyon satır için 10 milyon karşılaştırma.
Index, bu arama problemini O(log n)'e indirger. Kitabın sonundaki dizin gibi: "email" sütunundaki değerleri sıralı tutar ve her değerin fiziksel konumuna işaret eder.
-- Index olmadan: 10 milyon satır taranır SELECT * FROM users WHERE email = 'ali@example.com'; -- Index oluştur CREATE INDEX idx_users_email ON users(email); -- Index ile: B-tree traversal, ~20 adım SELECT * FROM users WHERE email = 'ali@example.com';
B-Tree: Index'lerin Arkasındaki Yapı
Çoğu veritabanının kullandığı index yapısı B-tree (Balanced Tree)'dir. Sıralı veriyi dengeli bir ağaç yapısında tutar.
[M]
/ \
[D, H] [R, V]
/ | \ / | \
[A] [E] [J] [N] [S] [X]
Arama her zaman kökten başlar. Her düğümde "daha küçük mü, eşit mi, daha büyük mü?" kararı verilir ve ilgili dala inilir. 1 milyar kayıtta bile maksimum ~30 adım.
B-tree sadece eşitlik sorgularında değil, aralık sorgularında da etkilidir:
-- B-tree bu sorguları verimli çözer SELECT * FROM orders WHERE amount > 1000; SELECT * FROM users WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'; SELECT * FROM products WHERE name LIKE 'iPhone%'; -- Önek araması
Index Türleri
Single Column Index — tek sütun üzerine:
CREATE INDEX idx_email ON users(email); CREATE INDEX idx_created ON orders(created_at);
Composite (Multi-Column) Index — birden fazla sütun üzerine:
-- Sıralama önemli: (last_name, first_name) ile -- WHERE last_name = ? → index kullanır -- WHERE first_name = ? → index KULLANMAZ -- WHERE last_name = ? AND first_name = ? → index kullanır CREATE INDEX idx_name ON users(last_name, first_name);
Composite index'te sütun sırası kritiktir. Index soldan sağa doğru kullanılır. İlk sütun olmadan sonraki sütunlar için index devreye girmez.
Unique Index — değerlerin benzersizliğini garanti eder:
CREATE UNIQUE INDEX idx_unique_email ON users(email); -- PRIMARY KEY otomatik olarak unique index oluşturur
Partial Index — tablonun yalnızca bir alt kümesi üzerine:
-- Sadece aktif kullanıcıları index'le — daha küçük, daha hızlı CREATE INDEX idx_active_users ON users(email) WHERE active = true; -- Sadece tamamlanmamış siparişler CREATE INDEX idx_pending ON orders(created_at) WHERE status = 'pending';
Covering Index — sorgunun ihtiyaç duyduğu tüm sütunları içerir, tabloya hiç gitmez:
-- Bu sorgu için covering index SELECT email, name FROM users WHERE department = 'engineering'; CREATE INDEX idx_dept_covering ON users(department, email, name); -- Veritabanı sadece index'i okur, tabloya gitmez
EXPLAIN: Sorgunun Planını Okumak
Index'in kullanılıp kullanılmadığını anlamanın yolu EXPLAIN komutudur:
EXPLAIN SELECT * FROM users WHERE email = 'ali@example.com';
id | select_type | table | type | key | rows | Extra
1 | SIMPLE | users | ref | idx_users_email | 1 | Using index
type sütunu kritiktir:
constveyaeq_ref— mükemmel, tek satırref— iyi, index kullanılıyorrange— kabul edilebilir, aralık taramasıindex— tüm index taranıyor, dikkatALL— tam table scan, index yok veya kullanılmıyor
-- Daha detaylı analiz EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123 AND status = 'completed' ORDER BY created_at DESC LIMIT 10;
Index Kullanılmayan Durumlar
Index oluşturuldu ama sorgu hâlâ yavaş? Muhtemelen şu hatalardan biri yapılıyor:
Sütun üzerinde fonksiyon kullanımı:
-- Index KULLANMAZ: sütun transform edildi SELECT * FROM users WHERE LOWER(email) = 'ali@example.com'; SELECT * FROM orders WHERE YEAR(created_at) = 2024; -- Index KULLANIR: sütun olduğu gibi bırakıldı SELECT * FROM users WHERE email = 'ali@example.com'; SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
LIKE ile önek araması:
-- Index KULLANIR: önek araması SELECT * FROM products WHERE name LIKE 'iPhone%'; -- Index KULLANMAZ: önek yok SELECT * FROM products WHERE name LIKE '%phone'; SELECT * FROM products WHERE name LIKE '%phone%';
Implicit type conversion:
-- user_id INTEGER sütunu ama string veriliyor → index KULLANMAZ SELECT * FROM users WHERE user_id = '123'; -- Doğru kullanım SELECT * FROM users WHERE user_id = 123;
OR koşulları:
-- Her iki sütunda ayrı index varsa bile OR index'i atlatabilir SELECT * FROM users WHERE email = 'a@b.com' OR phone = '555-1234'; -- Çözüm: UNION ALL SELECT * FROM users WHERE email = 'a@b.com' UNION ALL SELECT * FROM users WHERE phone = '555-1234';
Index'in Maliyeti
Index sihir değil. Her index bir bedel taşır:
Write maliyeti: Her INSERT, UPDATE, DELETE işleminde index de güncellenir. Çok sayıda index olan tablolarda yazma işlemleri yavaşlar.
Disk alanı: Her index ek depolama alanı kullanır. Büyük tablolarda index'ler toplam veri boyutunu ikiye katlayabilir.
Index bloat: Sürekli silme/güncelleme yapılan tablolarda index zamanla parçalanır. Periyodik REINDEX veya VACUUM gerekebilir.
Kural: okunan sütunları değil, filtrelenen, sıralanan ve join edilen sütunları index'leyin. Her sütuna index eklemek yerine, yavaş sorguları tespit edin ve onlar için index oluşturun.
N+1 Problemi ve Index
ORM kullanırken sıklıkla karşılaşılan N+1 problemi, index ile çözülmez — ama index ile birleşince etkisi azalır.
// N+1: Her kullanıcı için ayrı sorgu — 1001 sorgu const users = await User.findAll(); // 1 sorgu for (const user of users) { const orders = await Order.findAll({ where: { userId: user.id } }); // N sorgu } // Doğru: Tek sorguda JOIN const users = await User.findAll({ include: [{ model: Order }] // 1 veya 2 sorgu });
orders.user_id sütununda index varsa JOIN sorgusu çok daha hızlı çalışır. Foreign key sütunları neredeyse her zaman index'lenmeli.
Index doğru kurulduğunda veritabanı darboğazı olmaktan çıkar. Yanlış kurulduğunda hem okuma hem yazma işlemleri yavaşlar. Fark, hangi sorguları çalıştırdığınızı bilmek ve onları destekleyen index'leri tasarlamaktır.