Top Banner
Django ORM Optimizasyonu
49

Django ORM Optimizasyonu

Jun 25, 2015

Download

Documents

Fatih Erikli

Django ORM optimizasyonu hakkında bir sunum

Python İstanbul
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
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