Eğitim seçeneklerini yan yana koyup karar verin

Veritabanı Indexing Stratejileri ve Sorgu Optimizasyonu

Veritabanı Indexing Stratejileri ve Sorgu Optimizasyonu

Veritabanı performansı, yazılım geliştirme ekiplerinin sıkça karşılaştırmak zorunda kaldığı kritik bir meseledir. Milyonlarca kayıt arasından milisaniyeler içinde veri çekmek gerektiğinde, doğru indexing stratejisine sahip olmak ile olmamak arasındaki fark muazzam olur. Database index performans, sadece yazılımcılar için değil, projenin tamamlanma süresi, sunucu maliyeti ve kullanıcı deneyimi açısından da belirleyici bir faktöre dönüşür. Bu rehberde, B-tree indeksler, query plans ve N+1 sorunu çözme tekniklerini yan yana koyarak inceleyeceğiz—böylece teknoloji seçiminde ne gibi kriterler dikkate almanız gerektiğini görebilirsiniz.

B-Tree İndeksleri: Yapı ve Uygulamada Farklar

B-tree (Balanced Tree) indeksleri, ilişkisel veritabanlarında en yaygın şekilde kullanılan yapıdır. PostgreSQL, MySQL ve Oracle gibi sistemlerin çoğu varsayılan olarak B-tree indekslerine dayanır. Bu yapının temel avantajı, dengeli ve öngörülebilir performans sunmasıdır. Verilerin büyüklüğü iki katına çıksa bile, arama işleminin gerçekleştirilmesi için gerekli adım sayısı logaritmik ölçekte artar.

Ancak B-tree indeksleri tek çözüm değildir. Aralık aramaları (range queries) yapılacaksa B-tree mükemmeldir, fakat tam metin araması (full-text search) gerçekleştirmek istiyorsanız Hash indeksleri ya da özel metin indeksleri daha verimli olabilir. Coğrafi verileri sorgulamak için ise GiST ya da BRIN indeksleri daha iyi sonuç verir.

  • B-Tree: Eşitlik, aralık ve sıralama işlemleri için uygun; en çok bellek tüketir
  • Hash: Sadece eşitlik aramaları için; daha hızlı ama sınırlı
  • GiST: Karmaşık veri tipleri (metin, geometri) için; esnek ama yavaş
  • BRIN: Çok büyük veri setleri için; minimum bellek kullanır

Proje başlangıcında hangi indeks türünü seçeceğiniz, daha sonraki performans sorunlarının çoğunu önler. Eğer ne seçeceğinizden emin değilseniz, B-tree ile başlamak ve ardından sorgu yavaşlığını tespit ettikten sonra değiştirmek akılcı bir yaklaşımdır.

Query Plans Okumak ve Sorguları Optimize Etmek

Veritabanı optimization dünyasında "görmeden bilmezsin" ilkesi vardır. Bir sorgunun neden yavaş çalıştığını anlamanın en etkili yolu, sorgu yürütme planını (query plan) incelemektir. PostgreSQL'de EXPLAIN ANALYZE komutu, MySQL'de EXPLAIN FORMAT=JSON seçeneği, her bir adımda kaç satırın tarandığını, hangi indekslerin kullanıldığını ve tahmini maliyeti gösterir.

Query plan analizi yapılırken dikkat edilecek noktalar şunlardır:

  • Seq Scan vs Index Scan: Seq Scan (sıralı tarama) tüm tabloyu satır satır okur; Index Scan ise indeks yoluyla doğrudan ilgili satırlara erişir. Index Scan'in maliyeti düşükse, indeks başarıyla kullanılıyor demektir
  • Actual vs Planned Rows: Planlanan satır sayısı ile gerçek satır sayısı çok farklıysa, istatistikler güncel değildir; tablonun istatistikleri yeniden hesaplanmalıdır
  • Join Order: Birden fazla tablo birleştiriliyorsa, veritabanı hangi tabloyu önce filtrelediğini görmek önemlidir; küçük tabloyu önce filtrelemek çoğu zaman daha hızlıdır

Örneğin, 10 milyonluk bir müşteri tablosundan 5 milyonluk bir sipariş tablosuna katılım yapılırken, müşteri tablosunu önce filtrelemek (örneğin "ülke = Türkiye") çok daha az veriyle join işlemine başlayacağını anlamak, query plan analizi ile mümkün olur.

N+1 Sorunu ve Çözüm Yöntemleri

N+1 sorunu, yazılımcıların uygulama katmanında yaptığı bir hatanın database index performansını olumsuz etkilemesidir. Örneğin, 100 siparişi listelemek için önce 1 sorgu çalıştırılır (N), sonra her sipariş için müşteri bilgisini almak için 100 sorgu daha çalıştırılır (N×1 = 101 sorgu toplam).

Bu sorunun çözüm yolları şunlardır:

  1. JOIN ile Birleştirme: Baştan tek bir sorguyla gerekli tüm verileri çekmek. Örneğin, siparişleri müşteri bilgileriyle birlikte almak için SELECT * FROM siparis JOIN musteri ON siparis.musteri_id = musteri.id yapısı kullanılır. Bu, 101 sorguyu 1'e indirir
  2. Eager Loading: ORM (Object-Relational Mapping) araçları kullanıyorsanız, ilişkili verileri önceden yüklemek mümkündür. Django'da select_related() ya da prefetch_related(), SQLAlchemy'de joinedload() bu işi yapar
  3. Batch Loading: Eğer JOIN yapılamıyorsa, en azından veri tabanına 101 sorgu yapmak yerine 2 sorgu yapılabilir: önce tüm siparişler, sonra bir sorguyla tüm müşteri kimlikleri için bilgiler çekilir

Hangi yaklaşımı seçeceğiniz, uygulamanızın mimarisine bağlıdır. Monolitik bir REST API'si varsa JOIN yeterlidir. Ancak microservices mimarisinde, müşteri bilgileri farklı bir servisten geliyorsa batch loading veya caching gerekli olabilir.

Pratik Optimization Kontrol Listesi

  • Sık sorgulanan kolonlara indeks ekleyin—ama her kolona indeks eklemeyin (write işlemlerini yavaşlatır)
  • Bileşik indeksler (composite indexes) kullanırken sırayı dikkatle belirleyin; en sık filtrelenen kolon önce gelmelidir
  • Query plan analizi yaparak N+1 sorunlarını tespit edin
  • Tablo istatistiklerini düzenli olarak güncelleyin
  • Yavaş sorguları log'a kaydettirin ve periyodik olarak kontrol edin

Veritabanı optimizasyonu, bir kerelik işlem değil, sürekli bir izleme ve iyileştirme sürecidir. Doğru indexing stratejisi, uygun query plan analizi ve N+1 sorununun erken tespiti birleştiğinde, uygulamalarınız büyüdüğü halde performans problemleriyle karşılaşmayabilirsiniz. Öğrenme aşamasında hangi araç ve tekniği tercih edeceğiniz kararı vermeden önce, çeşitli kaynakları karşılaştırmak ve small-scale testler yapmak, yanlış yönde ilerlemenizi önler.