Index (Dizin) ve Performans

Index türleri, EXPLAIN ANALYZE ve sorgu optimizasyonu

Index Nedir?

Index, verilere hızlı erişim sağlayan veri yapılarıdır. Kitaptaki içindekiler gibi düşünün.

Index Türleri

TürKullanımOperatörler
B-tree (varsayılan)Eşitlik, aralık=, <, >, <=, >=, BETWEEN
HashSadece eşitlik=
GiSTGeometrik, full-text@>, &&, <@
GINArray, JSONB, full-text@>, ?, ?|, ?&
BRINBüyük sıralı tablolarZaman serisi

Index Oluşturma

-- Basit B-tree index
CREATE INDEX idx_users_email ON users(email);

-- Unique index
CREATE UNIQUE INDEX idx_users_username ON users(username);

-- Composite index (çoklu kolon)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at DESC);

-- Partial index (koşullu)
CREATE INDEX idx_orders_pending ON orders(created_at)
WHERE status = 'pending';

-- Expression index
CREATE INDEX idx_users_email_lower ON users(LOWER(email));

-- JSONB için GIN index
CREATE INDEX idx_documents_data ON documents USING GIN(data);
CREATE INDEX idx_documents_tags ON documents USING GIN(data -> 'tags');

-- Full-text search için GIN
CREATE INDEX idx_articles_search ON articles 
USING GIN(to_tsvector('turkish', title || ' ' || content));

CONCURRENTLY - Production'da Index

-- Tabloyu kilitlemeden index oluştur
CREATE INDEX CONCURRENTLY idx_large_table_col ON large_table(column);

-- Daha uzun sürer ama tabloyu kullanılabilir tutar
CONCURRENTLY transaction içinde kullanılamaz ve başarısız olursa INVALID index bırakır.

EXPLAIN ANALYZE

EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
Index Scan using idx_users_email on users (cost=0.29..8.30 rows=1 width=142) Index Cond: (email = 'test@example.com'::text) Planning Time: 0.152 ms Execution Time: 0.089 ms

Plan Okuma

TerimAçıklamaDurum
Seq ScanTüm tabloyu tarar⚠️ Index eksik olabilir
Index ScanIndex kullanır, tabloya da gider✅ İyi
Index Only ScanSadece index yeterli✅ En iyi
Bitmap ScanÇoklu koşul, index birleşimi✅ Normal
Nested Loopİç içe döngü join⚠️ Küçük tablolar OK
Hash JoinHash tabanlı join✅ Büyük tablolar
Merge JoinSıralı veri join✅ Sıralı veriler

Sorgu Optimizasyonu

-- KÖTÜ: Index kullanımını engeller
SELECT * FROM users WHERE LOWER(email) = 'test@example.com';
SELECT * FROM orders WHERE YEAR(created_at) = 2024;

-- İYİ: Index kullanır
SELECT * FROM users WHERE email ILIKE 'test@example.com';
SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

-- KÖTÜ: SELECT *
SELECT * FROM orders WHERE user_id = 5;

-- İYİ: Sadece gerekli kolonlar
SELECT id, total_amount, status FROM orders WHERE user_id = 5;

Index Bakımı

-- Index boyutları
SELECT 
    indexname,
    pg_size_pretty(pg_relation_size(indexname::regclass)) AS size
FROM pg_indexes
WHERE tablename = 'users';

-- Kullanılmayan indexler
SELECT 
    schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;

-- Index yeniden oluştur
REINDEX INDEX idx_users_email;
REINDEX TABLE users;