django - 總結 - ORM性能

QuerySet

一、惰性查詢python

二、緩存機制mysql

三、可迭代sql

四、可切片數據庫

-------------------------------------------------------
在一個新建立的查詢集中,緩存爲空。首次對查詢集進行求值 —— 同時發生數據庫查詢 ——Django
將保存查詢的結果到查詢集的緩存中並返回明確請求的結果(例如,若是正在迭代查詢集,則返回下一個結果)。
接下來對該查詢集 的求值將重用緩存的結果。

注意,當迭代完之後,第二次查詢,仍會操做數據庫
什麼時候查詢集不會被緩存?
1、重複獲取查詢集對象中一個特定的索引將每次都查詢數據庫
2、已經對所有查詢集求過值,則將檢查緩存
3、一些其它例子,它們會使得所有的查詢集被求值並填充到緩存中
queryset = Entry.objects.all()
[entry for entry in queryset] # Queries the database
print queryset[5] # Uses cache
print queryset[5] # Uses cache
四、簡單地打印查詢集不會填充緩存。

exists()與iterator()方法

exists:   只是簡單判斷queryset集是否有數據,但並不須要這些數據,用existsdjango

iterator:當數據量很是龐大時,一次性裝入內存是很是糟糕的,緩存

    iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存,     迭代器協議ide

注意:雖然iterator能夠防止生成cache,但意味着遍歷同一個queryset時會重複執行查詢因此使 #用iterator()性能

    的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢。fetch

  

中介模型

就是多對多模型的第三張表,有時咱們不只須要有兩個foreign key,還要有一些其餘的信息,所以咱們須要添加一些其餘的字段。優化

那麼此時咱們就不能用add,set,create這些方法來給第三張表添加數據

此時那麼咱們就須要用

m1=Book2auth(person=paul, group=beatles,date_joined=date(196081))

m1.save()

m2  =  Book2auth.objects.create(person = paul, group = beatles, date_joined = date( 1960 8 1 ))

這兩種方法來添加了 

select_related與prefetch_related與defer與only

與性能有關

models.User.objects.all().only('id','name','age')        # 只取
models.User.objects.all().defer('id','name','age')       # 排除
only,defer 查出來的也是queryset類型,包含一個個obj
查出來的是queryset類型,其中包含一個個對象,但當獲取obj之外的字段外,會另外發起一次數據庫查詢

 

 select_related用於一對一和外鍵查詢是對QuerySet進行了優化,會沿着外鍵關係查詢對象的數據,但會生成一個複雜的查詢並引發性能的損耗,但之後在使用外鍵關係時不須要數據庫查詢(join連表操做)

  1. 多外鍵查詢
    article=models.Article.objects.select_related("category").get(nid=1)
    print(article.articledetail)                兩次查詢
    article=models.Article.objects.select_related("category","articledetail").
    get(nid=1) print(article.articledetail) 查詢一次
  2. 深層查詢
    article=models.Article.objects.select_related("blog").get(nid=1)
        print(article.blog.user.username)      依然要查詢兩次
    由於第一次沒有查詢到userinfo表
    
    article=models.Article.objects.select_related("blog__user").get(nid=1)
    print(article.blog.user.username)
    一次
    select_related主要針一對一和多對一關係進行優化。
    select_related使用SQL的JOIN語句進行優化,經過減小SQL查詢的次數來進行優化、提升性能。
    能夠經過可變長參數指定須要select_related的字段名。也能夠經過使用雙下劃線「__」鏈接字段名來實現指定的遞歸查詢。
    沒有指定的字段不會緩存,沒有指定的深度不會緩存,若是要訪問的話Django會再次進行SQL查詢。
    也能夠經過depth參數指定遞歸的深度,Django會自動緩存指定深度內全部的字段。若是要訪問指定深度外的字段,Django會再次進行SQL查詢。
    也接受無參數的調用,Django會盡量深的遞歸查詢全部的字段。但注意有Django遞歸的限制和性能的浪費。
    Django >= 1.7,鏈式調用的select_related至關於使用可變長參數。Django < 1.7,鏈式調用會致使前邊的select_related失效,只保留最後一個。
    總結

prefetch_related()  子查詢

對於多對多字段(ManyToManyField)和一對多字段,可使用prefetch_related()來進行優化。

prefetch_related()和select_related()的設計目的很類似,都是爲了減小SQL查詢的數量,可是實現的方式不同。後者是經過JOIN語句,在SQL查詢內解決問題。可是對於多對多關係,使用SQL語句解決就顯得有些不太明智,由於JOIN獲得的表將會很長,會致使SQL語句運行時間的增長和內存佔用的增長。如有n個對象,每一個對象的多對多字段對應Mi條,就會生成Σ(n)Mi 行的結果表。

prefetch_related()的解決方法是,分別查詢每一個表,而後用Python處理他們之間的關係。

屢次單表操做,先查詢想要的數據,而後構造條件,如:id=[1,2,3],再次查詢其餘表根據id作條件
# 查詢全部文章關聯的全部標籤
    article_obj=models.Article.objects.all()
    for i in article_obj:
        print(i.tags.all())  #4篇文章: hits database 5

改成prefetch_related:

# 查詢全部文章關聯的全部標籤
    article_obj=models.Article.objects.prefetch_related("tags").all()
    for i in article_obj:
        print(i.tags.all())  #4篇文章: hits database 2

 

extra

models.Book.objects.extra(
    select={"standard_time": "strftime('%%Y-%%m--%%d',create_time)'"},  
      # 能夠再select語句中添加其餘字段信息    select *** from *** as B select_params=(), where=['nid in (1,3) OR title like "%s"', 'nid>2'], # 各個條件之間爲且的關係 params=["py", ], # 與where對應 tables=None, order_by=["-nid", ], )

raw

name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
# 若是SQL是其餘表時,必須將名字設置爲當前UserInfo對象的主鍵列名 models.Book.objects.raw("select id as nid from asd_userinfo",params=[],translations=name_map)  # 對應關係
- 原生SQL

from django.db import connection, connections
cursor = connection.cursor()  # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..)
PS: 選擇數據庫
queryset = models.Course.objects.using('default').all()  # setting DATABASES  是一個字典,裏面默認只有default一個數據庫。

日期截斷

在獲取日期對象時截取年月
data_format(obj,"%Y-%m"#   針對mysql
strftime("%%Y-%%m",obj)               #    針對sqlite
相關文章
相關標籤/搜索