Django 數據庫查詢優化


SQL Debug日志開啟

LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
View Code

 

  返回一個QuerySet,當執行它的查詢時它沿着外鍵關系查詢關聯的對象的數據。它會生成一個復雜的查詢並引起性能的損耗,但是在以后使用外鍵關系時將不需要數據庫查詢。

  ***對於一對一字段(OneToOneField)和外鍵字段(ForeignKey),可以使用select_related 來對QuerySet進行優化,在對QuerySet使用select_related()函數后,Django會獲取相應外鍵對應的對象,從而在之后需要的時候不必再查詢數據庫了。

 

示例1:

  標准的查詢:

# Hits the database. e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. b = e.blog

  select_related查詢:

# Hits the database. e = Entry.objects.select_related('blog').get(id=5) # Doesn't hit the database, because e.blog has been prepopulated # in the previous query. b = e.blog

 

示例二:

Host.objects.select_related("business").filter(user__username=user_name).count()

 

小結

 

1.select_related主要針一對一和多對一關系進行優化。
2.select_related使用SQL的JOIN語句進行優化,通過減少SQL查詢的次數來進行優化、提高性能。
3.可以通過可變長參數指定需要select_related的字段名。也可以通過使用雙下划線“__”連接字段名來實現指定的遞歸查詢。沒有指定的字段不會緩存,沒有指定的深度不會緩存,如果要訪問的話Django會再次進行SQL查詢。
4.也可以通過depth參數指定遞歸的深度,Django會自動緩存指定深度內所有的字段。如果要訪問指定深度外的字段,Django會再次進行SQL查詢。
5.也接受無參數的調用,Django會盡可能深的遞歸查詢所有的字段。但注意有Django遞歸的限制和性能的浪費。

 

  這允許它預取多對多和多對一對象,因為它們不能使用select_related來完成。

   *** select_related主要是執行跨表操作,而prefetch_related不是跨表操作,而是對每個表分別執行SQL查詢(Django內部在您使用時自動會幫你像跨表操作一樣連接起來),從而減少跨表查詢,提升性能。

小結

  1. 因為select_related()總是在單次SQL查詢中解決問題,而prefetch_related()會對每個相關表進行SQL查詢,因此select_related()的效率通常比后者高。
  2. 鑒於第一條,盡可能的用select_related()解決問題。只有在select_related()不能解決問題的時候再去想prefetch_related()。
  3. 你可以在一個QuerySet中同時使用select_related()和prefetch_related(),從而減少SQL查詢的次數。
  4. 只有prefetch_related()之前的select_related()是有效的,之后的將會被無視掉。

 

理解緩存屬性

  和整個QuerySet的緩存相同,ORM對象的屬性的結果中也存在緩存。通常來說,不可調用的屬性會被緩存。例如下面的博客模型示例

>>> entry = Entry.objects.get(id=1) >>> entry.blog   # Blog object is retrieved at this point
>>> entry.blog   # cached version, no DB access

 

  但是通常來講,可調用的屬性每一次都會訪問數據庫。

>>> entry = Entry.objects.get(id=1) >>> entry.authors.all()   # query performed
>>> entry.authors.all()   # query performed again

 

使用iterator()

當你有很多對象時,QuerySet的緩存行為會占用大量的內存。這種情況下,采用iterator()解決。

 

用唯一的被索引的列來檢索獨立對象

  有兩個原因在get()中,用帶有unique或者db_index的列檢索獨立對象。首先,由於查詢經過了數據庫的索引,所以會更快。其次,如果很多對象匹配查詢,查詢會更慢一些;列上的唯一性約束確保這種情況永遠不會發生。

 

使用QuerySet.values()values_list()

  當你僅僅想要一個帶有值的字典或者列表,並不需要使用ORM模型對象時,可以適當使用values()對於在模板代碼中替換模型對象,這樣會非常有用 —— 只要字典中帶有的屬性和模板中使用的一致,就沒問題。

  

使用QuerySet.defer()only()

  如果一些數據庫的列你並不需要(或者大多數情況下並不需要),使用defer()only()來避免加載它們。注意如果你確實要用到它們,ORM會在另外的查詢之中獲取它們。如果你不能夠合理地使用這些函數,不如不用。

 

使用QuerySet.count()

...如果你想要獲取大小,不要使用 len(queryset)

使用QuerySet.exists()

...如果你想要知道是否存在至少一個結果,不要使用if queryset

user_obj = UserInfo.objects.filter(username=login_obj.cleaned_data["login_username"], password=login_obj.cleaned_data["login_password"]) if user_obj.exists(): return HttpResponse(json.dumps(response))

 

直接使用外鍵的值

  如果你僅僅需要外鍵當中的一個值,要使用對象上你已經取得的外鍵的值,而不是獲取整個關聯對象再得到它的主鍵。例如,執行:

    entry.blog_id 

  而不是:

    entry.blog.id 

整體插入

創建對象時,盡可能使用bulk_create()來減少SQL查詢的數量。例如:

Entry.objects.bulk_create([ Entry(headline="Python 3.0 Released"), Entry(headline="Python 3.1 Planned") ])

...更優於:

Entry.objects.create(headline="Python 3.0 Released") Entry.objects.create(headline="Python 3.1 Planned")

 

這也可以用在ManyToManyFields中,所以:

my_band.members.add(me, my_friend)

...更優於:

my_band.members.add(me)
my_band.members.add(my_friend)

 

使用QuerySet.update()delete()

  通過QuerySet.update()使用批量的SQL UPDATE語句,而不是獲取大量對象,設置一些值再單獨保存。與此相似,在可能的地方使用批量deletes

 

 

1111


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM