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ür | Kullanım | Operatörler |
|---|---|---|
| B-tree (varsayılan) | Eşitlik, aralık | =, <, >, <=, >=, BETWEEN |
| Hash | Sadece eşitlik | = |
| GiST | Geometrik, full-text | @>, &&, <@ |
| GIN | Array, JSONB, full-text | @>, ?, ?|, ?& |
| BRIN | Büyük sıralı tablolar | Zaman 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
| Terim | Açıklama | Durum |
|---|---|---|
| Seq Scan | Tüm tabloyu tarar | ⚠️ Index eksik olabilir |
| Index Scan | Index kullanır, tabloya da gider | ✅ İyi |
| Index Only Scan | Sadece 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 Join | Hash tabanlı join | ✅ Büyük tablolar |
| Merge Join | Sı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;