YOU ARE DOWNLOADING DOCUMENT

Please tick the box to continue:

Transcript
Page 1: Django ORM Optimizasyonu

Django ORMOptimizasyonu

Page 2: Django ORM Optimizasyonu

Fatih ErikliPython & Django geliştiricisiyim.Hipo'da çalışıyorum.

http://fatiherikli.comhttp://github.com/fatiherikli/http://twitter.com/fthrkl/

Page 3: Django ORM Optimizasyonu

Optimizasyon

ORM optimizasyonundan önce standart veritabanı optimizasyonları yapılmalıdır.

Page 4: Django ORM Optimizasyonu

● İndeksler

● Uygun veri tipleri

● ...

Page 5: Django ORM Optimizasyonu

Profilleme

Sonraki iş ise hangi sorguların optimize edileceğine karar vermektir.

Page 6: Django ORM Optimizasyonu

● Django-debug-toolbar

● Sql printing middleware

● Django test case

Page 7: Django ORM Optimizasyonu

Queryset'leri anlamak

Page 8: Django ORM Optimizasyonu

Queryset'ler tembeldirler

Bu güzel bir şey.

Page 9: Django ORM Optimizasyonu

Queryset'ler tembeldirler

Sadece çalışması gerektiği zaman çalışırlar.

Page 10: Django ORM Optimizasyonu

>>> qs = Foo.objects.all()

>>> qs = qs.filter(published=True)

Çalışan sorgu sayısı: 0

Page 11: Django ORM Optimizasyonu

Ne zaman çalışırlar

● boolean(qs)● repr(qs)● qs[10:20]● len(qs)● iter(qs)

Page 12: Django ORM Optimizasyonu

Queryset'ler tembeldirler

Aynı şeyleri tekrar tekrar hesaplamak yerine zaten hesapladığı şeyi size tekrar verirler.

Page 13: Django ORM Optimizasyonu

>>> qs = Foo.objects.all()

>>> qs = qs.filter(published=True)

>>> list(qs)

....

>>> list(qs)

...

Çalışan sorgu sayısı: 1

Page 14: Django ORM Optimizasyonu

Foreign-key'ler de tembel

>>> post = Post.objects.get(id=1) # ilk sorgu

>>> post.category # ikinci sorgu

<Category: Personal>

>>> post.category # burada sorgu çalışmadı

<Category: Personal>

Page 15: Django ORM Optimizasyonu

N+1 sorgu problemi

Page 16: Django ORM Optimizasyonu

Queryset'leri listelerken yazdırılan her Foreign-Key şeklinde ilişkilendirilmiş kayıt için ayrı sorgu çalıştırılması durumu.

Page 17: Django ORM Optimizasyonu

Örnek: 10 adet blog post'umuz var

posts = Post.objects.all()

for post in posts:

print post.category

Çalışan sorgu sayısı: 11

Page 18: Django ORM Optimizasyonu

select_related

N+1 problemini çözmek için bu queryset metodu kullanılabilir.

Page 19: Django ORM Optimizasyonu

posts = Post.objects.select_related("category")

for post in posts:

print post.category

Çalışan sorgu sayısı: 1

Page 20: Django ORM Optimizasyonu

Many-to-Many One-to-Many problemleri

Page 21: Django ORM Optimizasyonu

Yine bir queryset'i listelerken bir kaydın diğer tablodaki birden çok kaydını yazdırmak istediğimizde karşımıza çıkan problem.

Page 22: Django ORM Optimizasyonu

Örnek: 10 adet blog post'umuz var.

posts = Post.objects.all()

for post in posts:

print post.tags.all()

Çalışan sorgu sayısı: 11

Page 23: Django ORM Optimizasyonu

prefetch_related

Bu problemi çözmek için ise bu queryset metodunu kullanabiliriz.

Page 24: Django ORM Optimizasyonu

Örnek: 10 adet blog post'umuz var.

posts = Post.objects.prefetch_related("tags")

for post in posts:

print post.tags.all()

Çalışan sorgu sayısı: 2

Page 25: Django ORM Optimizasyonu

Prefetch_related iki adet sorgu çalıştırıyor.

● İlk sorgu queryset'in kendi sorgusu● İkinci sorgu ise ilk sorguda aldığı id'ler ile

ilişkili tablodaki kayıtları getirmek için yaptığı `IN` sorgusu.

Page 26: Django ORM Optimizasyonu

Tek sorguya indirebilir miyiz?

Hayır, çünkü gerek yok.

Page 27: Django ORM Optimizasyonu

Sorgu Sayısı != Performans

Page 28: Django ORM Optimizasyonu

Gereksiz veriler

Page 29: Django ORM Optimizasyonu

Yine Django ORM ile çok fazla alakası olmayan bir durum. Optimizasyon yapılacak bir view'da gereksiz hiç bir veri bırakmamak gerekir.

Page 30: Django ORM Optimizasyonu

Ancak Django ORM'de bunu kolaylaştıracak bazı teknikler bulunmaktadır.

Page 31: Django ORM Optimizasyonu

values metodu

Sadece istediğiniz alanların verilerini dictionary şeklinde listesini getirir.

Page 32: Django ORM Optimizasyonu

posts = [{

"id": post.pk

"title": post.title

} for post in Post.objects.all()]

Pitonik :) fakat mükemmel değil.

Page 33: Django ORM Optimizasyonu

posts = Post.objects.values("id", "title")

# [{"id": 2, "title": "test"}, ... ]

Mükemmel

posts = Post.objects.values_list("id", flat=True)

# [2,3,4,5 ... ]

Page 34: Django ORM Optimizasyonu

Aggregation

Page 35: Django ORM Optimizasyonu

Aggregation (sum, count, average vb.) işlemleri her zaman veritabanı katmanında daha hızlıdır.

Page 36: Django ORM Optimizasyonu

posts = Post.objects.all()

print len(posts)

# tüm queryset çalıştırılıp python tarafında hesaplanır.

Aggregation sayılmaz, ancak önemli bir ayrıntı.

posts = Post.objects.all()

print posts.count()

# select count(1) from foobar;

# gibi bir sql sorgusu çalıştırılır.

Page 37: Django ORM Optimizasyonu

posts = Post.objects.all()

print len(posts)

# tüm queryset çalıştırılıp python tarafında hesaplanır.

Aggregation sayılmaz, ancak önemli bir ayrıntı.

posts = Post.objects.all()

print posts.count()

# select count(1) from foobar;

# gibi bir sql sorgusu çalıştırılır.

Page 38: Django ORM Optimizasyonu

categories = Category.objects.all()

for category in categories.all():

print category.name, category.posts.count()

Kötü bir örnek

Page 39: Django ORM Optimizasyonu

categories = Category.objects.prefetch_related("posts")

for category in categories.all():

print category.name, category.posts.count()

Biraz daha iyi

Page 40: Django ORM Optimizasyonu

categories = Category.objects.aggregate(

post_count = Count("posts")

)

for category in categories.all():

print category.name, category.post_count

Mükemmel

Page 41: Django ORM Optimizasyonu

Testing

Page 42: Django ORM Optimizasyonu

Sql sorgularını test etmek iyi bir pratik olabilir.

Page 43: Django ORM Optimizasyonu

django.db.queries

Testler Debug=False şeklinde çalıştığı için Django'nun sorgu loglarını bu şekilde alamazsınız.

Page 44: Django ORM Optimizasyonu

from django.test import TestCase

class FooTest(TestCase):

def test_foo(self):

self.assertNumQueries(0, Post.objects.all)

self.assertNumQueries(1, Post.objects.count)

def _test():

for post in Post.objects.all():

print post.title

self.assertNumQueries(1, _test)

Django'nun TestCase sınıfı ile bu problemi aşabilirsiniz.

Page 45: Django ORM Optimizasyonu

Ufak tefek optimizasyon ipuçları

Page 46: Django ORM Optimizasyonu

post.category.id # burada bir sorgu çalışır

post.category_id # bu şekilde alınırsa sorgu çalışmaz

Foreign-Key'ler veritabanında foo_id şeklinde saklanır.

Page 47: Django ORM Optimizasyonu

{% with posts.count as post_count %}

{% if post_count %}

{{ post_count }}

{% endif %}

{% endwith %}

Template üzerinde `with` ile caching yapabilirsiniz.

Page 48: Django ORM Optimizasyonu

from django.db.models import F

Post.objects.update(view_count = F("view_count") + 1)

`F` ile update ve filter metodunda modeldeki başka bir field'ı referans olarak gösterebilirsiniz.

Page 49: Django ORM Optimizasyonu

Teşekkürler


Related Documents