Uygulaman birkaç yüz kullanıcıyla harika çalışıyordu. Sonra trafik arttı ve sayfalar açılmaz oldu. Bu noktada sorun çoğu zaman sunucu gücü değil, yazılan kodun veritabanını nasıl kullandığıdır. Sağlam bir django backend kurmak, en baştan doğru alışkanlıklarla başlar.Bu yazıda Django ile ölçeklenebilir bir backend kurmanın temel taşlarını ele alıyoruz. ORM sorgularını sadeleştirmekten önbelleğe, arka plan görevlerinden veritabanı indekslemeye kadar pratik örnekler var. Hepsi gerçek projelerde fark yaratan tekniklerdir.
Django backend için ORM optimizasyonu
Çoğu yavaşlığın kaynağı tek bir sorundur: N+1 sorgu problemi. İlişkili nesneleri döngü içinde çağırdığında Django her satır için ayrı bir veritabanı sorgusu atar. Yüz kayıt yüz bir sorgu demektir.Çözüm select_related ve prefetch_related metotlarıdır. Birincisi tek bir JOIN ile ilişkili kaydı getirir, ikincisi çoka çok ilişkileri toplu çeker. Resmi belgelerdeki veritabanı optimizasyon rehberi bu konuyu örneklerle anlatıyor.
# Kötü: her yazar için ayrı sorgu
posts = Post.objects.all()
for post in posts:
print(post.author.name)
# İyi: tek JOIN ile hepsi
posts = Post.objects.select_related("author")
for post in posts:
print(post.author.name)Bir başka alışkanlık da sadece ihtiyacın olan alanları çekmektir. only() ve values() metotları gereksiz sütunları sorgudan çıkarır. Büyük tablolarda bu tercih bellek kullanımını ciddi şekilde düşürür.
Önbellek ile yükü azaltmak
Aynı veriyi her istekte yeniden hesaplamak boşa giden bir efordur. Önbellek, sonucu bir kez üretip tekrar tekrar sunmanı sağlar. Django bunun için Redis ve Memcached desteğiyle gelir.Önbelleği farklı seviyelerde uygulayabilirsin. Tüm sayfayı, bir görünümü ya da sadece pahalı bir sorgunun sonucunu saklayabilirsin. Aşağıdaki tablo yaygın seçenekleri özetliyor.
YöntemNe saklarNe zaman uygun
Sayfa önbelleği | Tüm HTTP yanıtını | Nadiren değişen statik sayfalar
Görünüm önbelleği | Bir view çıktısını | Belirli endpoint sonuçları
Düşük seviye önbellek | Tek bir değer veya sorgu | Pahalı hesaplamalar
Önbellekte en zor kısım veriyi ne zaman geçersiz kılacağını seçmektir. Kayıt güncellendiğinde ilgili anahtarı silmen gerekir. Aksi halde kullanıcıya eski veriyi göstermeye devam edersin.
Celery ile arka plan görevleri
Bazı işler bir HTTP isteğini bekletecek kadar uzundur. E-posta göndermek, rapor üretmek ya da görsel işlemek gibi. Bu işleri kullanıcının önünde yapmak yanıt süresini uzatır. Celery, bu görevleri ayrı bir işçi sürecine taşır.Mantık basittir. Uygulama görevi bir kuyruğa atar, hemen yanıt döner. Arka plandaki Celery işçisi görevi sırası gelince çalıştırır. Kuyruk için genelde Redis veya RabbitMQ kullanılır.
from celery import shared_task
@shared_task
def send_welcome_email(user_id):
user = User.objects.get(id=user_id)
# uzun süren e-posta gönderimi burada
return f"E-posta gonderildi: {user.email}"
# View içinde çağrı
send_welcome_email.delay(user.id)Bellek yoğun görevleri ayrı işçilere vermek de iyi bir fikirdir. Böylece bir görevin bellek tüketimi kullanıcıya bakan süreci etkilemez. İşçilerini periyodik yeniden başlatma politikasıyla ayarlayabilirsin.
DRF ile verimli API tasarımı
Django REST Framework hızlı API üretmeyi kolaylaştırır. Ama dikkatsiz kullanılırsa yavaşlığın kaynağı da olabilir. İlk kural büyük veri setlerini tek yanıtta göndermemektir.Sayfalama bunun standart çözümüdür. DRF hazır sayfalama sınıfları sunar; bunları aktif etmen yeterlidir. Serializer tarafında da sadece gereken alanları döndür. Ağır iç içe serializer'lar yanıt süresini katlar.
- Tüm listelerde sayfalama kullan, sınırsız sorgudan kaçın.
- Serializer alanlarını minimumda tut, gereksiz ilişkileri çıkar.
- I/O ağırlıklı işlemler için Django'nun async view desteğini değerlendir.
- Üretimde Sentry veya Prometheus ile yanıt sürelerini izle.
Bu adımlar küçük projelerde fark etmez. Ama trafik büyüdükçe her biri saniyeler kazandırır. İzleme olmadan nereyi optimize edeceğini de bilemezsin.
Veritabanı indeksleme
İndeks, veritabanının bir kaydı hızlı bulmasını sağlayan yapıdır. İndekssiz bir sorgu tüm tabloyu tarar. Milyonlarca satırda bu tarama saniyeler sürer.Sık filtrelediğin ve sıraladığın sütunlara indeks ekle. Django bunu model tanımında doğrudan destekler. Ama her sütuna indeks koymak da yanlıştır; indeksler yazma işlemlerini yavaşlatır ve disk yer kaplar.
class Post(models.Model):
slug = models.SlugField(db_index=True)
created = models.DateTimeField()
class Meta:
indexes = [
models.Index(fields=["created", "slug"]),
]Hangi sorgunun yavaş olduğunu görmek için EXPLAIN çıktısını incele. Django'nun django-debug-toolbar aracı geliştirme sırasında bunu kolaylaştırır. Önce ölç, sonra indeks ekle.
Gunicorn ve Nginx ile dağıtım
Django'nun geliştirme sunucusu üretim için uygun değildir. Gerçek dağıtımda Gunicorn uygulamayı çalıştırır, Nginx önünde durur. Nginx statik dosyaları sunar ve gelen isteği Gunicorn'a iletir.Gunicorn işçi sayısını sunucunun CPU çekirdeğine göre ayarlarsın. Yaygın bir başlangıç noktası çekirdek başına iki artı bir işçidir. Nginx ise SSL sonlandırma ve sıkıştırma gibi işleri üstlenir, böylece uygulama katmanı sade kalır.Bu yapıyı garantili CPU ve RAM sunan, NVMe diskli bir VPS üzerinde çalıştırmak performansı doğrudan etkiler. Kritm Cloud Solutions olarak hem özel yazılım tarafında Python ve Django backend geliştiriyoruz hem de bunu barındıracak bulut altyapısını sağlıyoruz. Hizmetlerimizi inceleyebilir, projen için bizimle iletişime geçebilirsin.Özetle ölçeklenebilir bir backend tek bir sihirli ayarla gelmez. ORM sorgularını sadeleştir, önbelleği akıllıca kullan, uzun işleri Celery'ye devret ve veritabanını indeksle. Önce ölç, sonra optimize et. Bu disiplin trafiğin büyüdüğünde seni rahat ettirir.
