Django ORM Optimizasyonu

1,399 views

Published on

Django ORM optimizasyonu hakkında bir sunum

Python İstanbul

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,399
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
11
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Django ORM Optimizasyonu

  1. 1. Django ORMOptimizasyonu
  2. 2. Fatih ErikliPython & Django geliştiricisiyim.Hipoda çalışıyorum.http://fatiherikli.comhttp://github.com/fatiherikli/http://twitter.com/fthrkl/
  3. 3. OptimizasyonORM optimizasyonundan önce standartveritabanı optimizasyonları yapılmalıdır.
  4. 4. ● İndeksler● Uygun veri tipleri● ...
  5. 5. ProfillemeSonraki iş ise hangi sorguların optimizeedileceğine karar vermektir.
  6. 6. ● Django-debug-toolbar● Sql printing middleware● Django test case
  7. 7. Querysetleri anlamak
  8. 8. Querysetler tembeldirlerBu güzel bir şey.
  9. 9. Querysetler tembeldirlerSadece çalışması gerektiği zaman çalışırlar.
  10. 10. >>> qs = Foo.objects.all() >>> qs = qs.filter(published=True)Çalışan sorgu sayısı: 0
  11. 11. Ne zaman çalışırlar● boolean(qs)● repr(qs)● qs[10:20]● len(qs)● iter(qs)
  12. 12. Querysetler tembeldirlerAynı şeyleri tekrar tekrar hesaplamak yerinezaten hesapladığı şeyi size tekrar verirler.
  13. 13. >>> qs = Foo.objects.all() >>> qs = qs.filter(published=True) >>> list(qs) .... >>> list(qs) ...Çalışan sorgu sayısı: 1
  14. 14. Foreign-keyler de tembel >>> post = Post.objects.get(id=1) # ilk sorgu >>> post.category # ikinci sorgu <Category: Personal> >>> post.category # burada sorgu çalışmadı <Category: Personal>
  15. 15. N+1 sorgu problemi
  16. 16. Querysetleri listelerken yazdırılan her Foreign-Key şeklinde ilişkilendirilmiş kayıt için ayrısorgu çalıştırılması durumu.
  17. 17. Örnek: 10 adet blog postumuz var posts = Post.objects.all() for post in posts: print post.categoryÇalışan sorgu sayısı: 11
  18. 18. select_relatedN+1 problemini çözmek için bu querysetmetodu kullanılabilir.
  19. 19. posts = Post.objects.select_related("category") for post in posts: print post.categoryÇalışan sorgu sayısı: 1
  20. 20. Many-to-ManyOne-to-Manyproblemleri
  21. 21. Yine bir queryseti listelerken bir kaydın diğertablodaki birden çok kaydını yazdırmakistediğimizde karşımıza çıkan problem.
  22. 22. Örnek: 10 adet blog postumuz var. posts = Post.objects.all() for post in posts: print post.tags.all()Çalışan sorgu sayısı: 11
  23. 23. prefetch_relatedBu problemi çözmek için ise bu querysetmetodunu kullanabiliriz.
  24. 24. Örnek: 10 adet blog postumuz var. posts = Post.objects.prefetch_related("tags") for post in posts: print post.tags.all()Çalışan sorgu sayısı: 2
  25. 25. Prefetch_related iki adet sorgu çalıştırıyor.● İlk sorgu querysetin kendi sorgusu● İkinci sorgu ise ilk sorguda aldığı idler ile ilişkili tablodaki kayıtları getirmek için yaptığı `IN` sorgusu.
  26. 26. Tek sorguya indirebilir miyiz?Hayır, çünkü gerek yok.
  27. 27. Sorgu Sayısı != Performans
  28. 28. Gereksiz veriler
  29. 29. Yine Django ORM ile çok fazla alakası olmayanbir durum. Optimizasyon yapılacak bir viewdagereksiz hiç bir veri bırakmamak gerekir.
  30. 30. Ancak Django ORMde bunu kolaylaştıracakbazı teknikler bulunmaktadır.
  31. 31. values metoduSadece istediğiniz alanların verilerini dictionaryşeklinde listesini getirir.
  32. 32. posts = [{ "id": post.pk "title": post.title} for post in Post.objects.all()]Pitonik :) fakat mükemmel değil.
  33. 33. posts = Post.objects.values("id", "title")# [{"id": 2, "title": "test"}, ... ]posts = Post.objects.values_list("id", flat=True)# [2,3,4,5 ... ]Mükemmel
  34. 34. Aggregation
  35. 35. Aggregation (sum, count, average vb.) işlemleriher zaman veritabanı katmanında daha hızlıdır.
  36. 36. posts = Post.objects.all()print len(posts)# tüm queryset çalıştırılıp python tarafında hesaplanır.posts = Post.objects.all()print posts.count()# select count(1) from foobar;# gibi bir sql sorgusu çalıştırılır.Aggregation sayılmaz, ancak önemlibir ayrıntı.
  37. 37. posts = Post.objects.all()print len(posts)# tüm queryset çalıştırılıp python tarafında hesaplanır.posts = Post.objects.all()print posts.count()# select count(1) from foobar;# gibi bir sql sorgusu çalıştırılır.Aggregation sayılmaz, ancak önemlibir ayrıntı.
  38. 38. categories = Category.objects.all()for category in categories.all(): print category.name, category.posts.count()Kötü bir örnek
  39. 39. categories = Category.objects.prefetch_related("posts") for category in categories.all(): print category.name, category.posts.count()Biraz daha iyi
  40. 40. categories = Category.objects.aggregate( post_count = Count("posts"))for category in categories.all(): print category.name, category.post_countMükemmel
  41. 41. Testing
  42. 42. Sql sorgularını test etmek iyi bir pratik olabilir.
  43. 43. django.db.queriesTestler Debug=False şeklinde çalıştığı içinDjangonun sorgu loglarını bu şekildealamazsınız.
  44. 44. from django.test import TestCaseclass 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)Djangonun TestCase sınıfı ile buproblemi aşabilirsiniz.
  45. 45. Ufak tefekoptimizasyon ipuçları
  46. 46. post.category.id # burada bir sorgu çalışırpost.category_id # bu şekilde alınırsa sorgu çalışmazForeign-Keyler veritabanında foo_idşeklinde saklanır.
  47. 47. {% with posts.count as post_count %} {% if post_count %} {{ post_count }} {% endif %}{% endwith %}Template üzerinde `with` ile cachingyapabilirsiniz.
  48. 48. from django.db.models import F Post.objects.update(view_count = F("view_count") + 1)`F` ile update ve filter metodundamodeldeki başka bir fieldı referansolarak gösterebilirsiniz.
  49. 49. Teşekkürler

×