Proje Ustalığıders 8/8
DERS 2.8 Sessiz katil: veritabanı index'i ve bulut maliyeti
KAZANIM Bu dersin sonunda: hiçbir hata vermeden faturanı katlayan eksik-index patolojisini kendi ellerinle teşhis eder, kalıcı biçimde düzelttirir ve bulut faturanı "compute-saat" diliyle okursun.
1TanımIndex = veritabanı tablosunun arka sayfasındaki dizin. Kitap analojisi birebirdir: "Atatürk hangi sayfalarda geçiyor?" sorusuna dizinli kitap 5 saniyede cevap verir; dizinsiz kitapta 400 sayfayı tek tek çevirirsiniz. Veritabanı da aynı iki yolu bilir: index scan (dizinden bak, doğru satıra atla) ve seq scan (sequential: tabloyu baştan sona tara). İkisi de aynı DOĞRU cevabı verir — farkları hız ve emektir. Kritik nokta: tabloyu oluşturunca yalnız kimlik kolonuna (primary key) otomatik dizin gelir; "bu ürünün yorumları" gibi İLİŞKİ kolonlarına (product_id tarzı foreign key'ler) dizini SİZİN eklemeniz gerekir.
2İşlev ve amaç — ve fatura bağlantısıBulut veritabanısı size sabit kira kesmez; motor gücü × çalışma saati faturalar (Neon örneği: CU "compute unit" × aktif-saat; motor trafiğe göre min–max arasında kendini büyütür [autoscaling], hiç iş yokken uyur [scale-to-zero]). Araba analojisi: eksik index, motoru her soruda boşa TAM GAZA bastırmaktır — yol aynı yol, varış aynı varış, ama 8 silindirin hepsi yanar. Bu yüzden eksik FK index'i belirtisiz bir patolojidir: küçük veride ve düşük trafikte tarama zaten milisaniyedir, hiçbir şey hissedilmez; veri ve trafik büyüdükçe her sorgu sessizce ağırlaşır. Uygulama ÇÖKMEZ, hata VERMEZ, kullanıcı belki fark etmez bile — tek belirti ay sonu faturasıdır. En tehlikeli bozulma sınıfı budur: sessiz ve kademeli.
3Nasıl yapılırTeşhis → tedavi → kalıcılaştırma:
Teşhis — Postgres'in kendi sayaçlarını okutun (çoğu bulutta ek kurulum istemeyen tablo):
SELECT relname AS tablo, seq_scan, seq_tup_read, idx_scan, n_live_tup AS satir
FROM pg_stat_user_tables ORDER BY seq_tup_read DESC LIMIT 10;
Okuma kılavuzu: seq_scan = kaç kez baştan sona tarandı · seq_tup_read = bu taramalarda TOPLAM kaç satır okundu · idx_scan = kaç kez dizin kullanıldı. Kırmızı kombinasyon: seq_tup_read, tablonun satır sayısının binlerce katı + idx_scan ~0 → o tablo her soruda baştan okunuyor. (Sorgu-bazlı ayrıntı veren pg_stat_statements çoğu ortamda kapalıdır — bu tablo neredeyse her yerde açıktır, önce bunu okuyun.)
Tedavi — eksik dizini ekletin; SQL'de tek satırdır: CREATE INDEX idx_reviews_product ON reviews (product_id);
Kalıcılaştırma — asıl disiplin burası. Kod ORM'le yazılıyorsa dizin, şemanın parçası olmalı: TypeORM'de ilişki kolonunun üstüne @Index(), Prisma'da modele @@index([productId]). Sonra migration (2.5 kuralları: additive, önce migration sonra kod) ile veritabanına işletin. Elle CREATE INDEX çekip şemaya yazmamak, yarın temiz kurulumda dizinin YOK olması demektir.
4Ne ters gider + çözümü
⚠ GERÇEK VAKA (Ağustos 2026, iki üründe aynı anda): Aylık bulut-veritabanı faturası $31'den $141'e sıçradı — 4,5 kat, hiçbir çökme/hata olmadan. Teşhis: iki ayrı üründe (biri TypeORM, biri Prisma) ilişki kolonlarında index yoktu; her katalog sorgusu tabloyu baştan sona tarıyordu. Bilanço akıl almazdı: 68 bin satırlık bir tablodan, sayaçlara göre 42 MİLYAR satır okunmuş, 3 günde 149 GB veri taşınmıştı. Kök neden bir "ORM halleder" varsayımıydı: TypeORM'in
@ManyToOne/@JoinColumn'u da Prisma'nın ilişki tanımı da FK kolonuna OTOMATİK index KOYMAZ — ikisi de belgelenmiş, ikisi de klasik tuzak. Düzeltme iki koldan geldi: index'ler migration'la eklendi (asıl tedavi) + motorun CU tavanı 8'den 1'e çekildi (hasar sınırlayıcı). Tavan kısmak yakıt deposuna kilit vurmaktır — faturayı sınırlar ama gaza basılı pedalı düzeltmez; ikisi birlikte yapılır.
Diğer tuzaklar: index'i yalnız lokalde eklemek → migration'a girmeyen şema değişikliği prod'da YOKTUR (2.5'in "entity var ≠ tablo var" dersiyle aynı aile); körlemesine her kolona index → her dizin yazma işlemini yavaşlatır ve yer yer; kural "her FK'ya evet, gerisi ölçüme göre"; faturayı görüp paniğe kapılıp motoru kapatmak → semptomu susturur, kullanıcı deneyimini öldürür, patoloji durur.
5AlternatiflerSemptom yönetimi araçları vardır ama hiçbiri index'in yerine geçmez: CU/instans tavanını kısmak (fatura sigortası — vaka bunu da yaptı), sık sorulan cevabı önbelleğe almak (cache), okuma yükünü kopyaya dağıtmak (read replica — ileri seviye). Sıralama hep aynıdır: önce index (kök neden), sonra sigortalar. Bir de proaktif alternatif var: bazı ORM/linter araçları FK-index taraması yapar — ama en taşınabilir yol, bu dersin denetim istemini periyodik koşturmaktır.
6Denetim penceresiKırmızı bayraklar: bulut faturası açıklamasız ay-üstü-ay %50+ arttı · panelde compute-saat/veri-transferi grafiği trafikle orantısız · pg_stat_user_tables'da seq_tup_read/satır oranı binleri aşıyor · şemada FK kolonu var, index'i yok. İkinci-ajan denetim istemi (temiz oturumda):
Bu projenin veritabanı şemasını denetle: 1) Tüm ilişki (foreign key) kolonlarını listele.
2) Her biri için index var mı — VAR/YOK tablosu çıkar. 3) YOK olanlar için migration
planı öner ama HİÇBİR ŞEYİ değiştirme. 4) pg_stat_user_tables sorgusunu çalıştırıp
en çok seq_tup_read biriktiren 5 tabloyu yorumla.
Ve kalıcı alışkanlık: bulut panelinde harcama alarmı kurun (ör. "aylık $X aşılırsa e-posta"). Bu vakada tek belirti faturaydı — alarm, faturayı ay sonundan güne çeker.
7Mini egzersiz (KENDİ-PROJEN)Üret: yukarıdaki denetim istemini veritabanı kullanan bir projenizde koşturun; YOK çıkan bir FK için ORM şemasına index ekletin + migration'la işletin. Denetle: migration'dan önce ve sonra pg_stat sorgusunu çalıştırıp idx_scan sayacının artmaya başladığını kendi gözünüzle görün (sayaç artıyorsa dizin gerçekten kullanılıyordur — "ekledim" ile "kullanılıyor" aynı şey değildir).
8Kontrol soruları(1) Seq scan ile index scan farkı nedir ve neden ikisi de "doğru" cevap verir? (2) Eksik FK index'i neden küçük projede görünmez? (3) ORM ilişki tanımı index'i otomatik koyar mı — bu varsayım neye mal oldu? (4) CU tavanını kısmak neden tek başına çözüm değildir?
9Mentor istemi
AI Maestro Seviye 2 Ders 2.8'in (index ve bulut maliyeti) hocası ol. Kitap dizini ve
tam-gaz motor analojileriyle başla. Bana uydurma bir pg_stat_user_tables çıktısı göster:
içine bir sessiz-katil tablo sakla, kırmızı kombinasyonu bulmamı iste. Sonra faturayı
CU×saat diliyle hesaplat. Sonunda 4 kontrol sorusu.