Yazılım
100%

Django ORM Mimarisinde N+1 Sorgu Problemi ve Çözümleri

Python Django framework'ünde veritabanı performansını felç eden N+1 sorgu probleminin tespiti ve select_related/prefetch_related kullanımı.

Genel Bakış

Django ORM (Object-Relational Mapping), Python geliştiricilerinin SQL yazmadan Python nesneleri üzerinden veritabanı ile etkileşime girmesini sağlayan inanılmaz güçlü bir araçtır. Ancak bu soyutlama, arka planda ne tür SQL sorguları atıldığını gizlediği için ciddi performans sorunlarına yol açabilir.

Sorun

İlişkisel veritabanlarında çalışırken sıkça karşılaşılan "N+1 Problemi", bir listeyi çekerken 1 adet sorgu atılması ve ardından listedeki her bir elemanın bağlı olduğu alt elemanları çekmek için döngü içinde N adet ekstra sorgu atılması durumudur.

Örneğin, 50 adet yazarımız ve onların yazdığı kitaplar olsun. Şablonunuzda (template) yazarları ve kitaplarını listelemek istediğinizde, Django önce yazarları çekmek için 1 sorgu atar. Sonra şablondaki for yazar in yazarlar döngüsü içinde yazar.kitap_set.all her çağrıldığında veritabanına yeni bir sorgu atar. 50 yazar için toplam 51 sorgu çalışır. Trafik arttığında bu durum veritabanını kilitler.

Çözüm: Eager Loading (İstekli Yükleme)

Bu sorunu çözmek için Django ORM'inin bize sunduğu iki sihirli metodu kullanmalıyız: select_related ve prefetch_related.

Optimize Edilmiş Sorgu Örneği:

# Kötü Kullanım: Yazarları çeker (1 sorgu), döngüde her kitap için tekrar sorgu atar.
yazarlar = Yazar.objects.all()

# İyi Kullanım: Django arkaplanda SQL JOIN işlemi yapar ve tüm veriyi tek 1 sorguda getirir.
yazarlar = Yazar.objects.select_related('kitap').all()

Teknik Detaylar

select_related(), OneToOneField veya ForeignKey ilişkilerinde (İleriye dönük Tekil ilişkiler) kullanılır. Veritabanı seviyesinde bir SQL INNER JOIN oluşturur ve veriyi tek bir devasa tabloda çeker.
prefetch_related() ise ManyToManyField veya geriye dönük ForeignKey (Bir yazarın birden çok kitabı olması) durumlarında kullanılır. SQL JOIN yapmaz; bunun yerine ayrı bir SQL sorgusu ile ilişkili tüm nesneleri çeker ve eşleştirme işlemini (merging) veritabanında değil, Python seviyesinde (RAM'de) gerçekleştirir.

Sonuç

Django projelerini canlıya (production) almadan önce Django Debug Toolbar kullanılarak her sayfanın kaç SQL sorgusu ürettiği incelenmelidir. Doğru select_related ve prefetch_related kullanımı, sayfa yükleme sürelerini saniyelerden milisaniyelere düşüren en önemli optimizasyon adımıdır.

İlgili Wiki'ler

Tümünü Gör