1. Django
的 QuerySet
是惰性的
Django
的 QuerySet
對應於資料庫的若干記錄(row
),透過可選的查詢來過濾。例如,下面的程式碼會得到資料庫中名字為 Dave
的所有的人:
person_set = Person.objects.filter(first_name="Dave")
上面的程式碼並沒有執行任何的資料庫查詢。你可以使用 person_set
,給它加上一些過濾條件,或者將它傳給某個函式,這些操作都不會傳送給資料庫。這是對的,因為資料庫查詢是顯著影響 web
應用效能的因素之一。
2. 真正從資料庫獲得資料
要真正從資料庫獲得資料,你可以遍歷 QuerySet
或者使用 if QuerySet
,總之你用到資料時就會執行 sql
。為了驗證這些,需要在 settings
里加入 LOGGING
,驗證方式:
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
# if obj:
# print("ok")
3. QuerySet
是具有 cache
的
當你遍歷 QuerySet
時,所有匹配的記錄會從資料庫獲取,然後轉換成 Django
的 model
。這被稱為執行(evaluation
)。這些 model
會儲存在 QuerySet
內建的 cache
中,這樣如果你再次遍歷這個 QuerySet
,你不需要重複執行通用的查詢。
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
## models.Book.objects.filter(id=3).update(title="GO")
## obj_new=models.Book.objects.filter(id=3)
# for i in obj:
# print(i) #LOGGING只會列印一次
4. 驗證 cache
中是否有資料
簡單的使用 if
語句進行判斷也會完全執行整個 QuerySet
並且把資料放入 cache
,雖然你並不需要這些資料!為了避免這個,可以用 exists()
方法來檢查是否有資料:
obj = Book.objects.filter(id=4)
# exists()的檢查可以避免資料放入queryset的cache。
if obj.exists():
print("hello world!")
5. 當 QuerySet
非常巨大時,cache
會成為問題
處理成千上萬的記錄時,將它們一次裝入記憶體是很浪費的。更糟糕的是,巨大的 QuerySet
可能會鎖住系統程序,讓你的程式瀕臨崩潰。要避免在遍歷資料的同時產生 QuerySet cache
,可以使用 iterator()
方法來獲取資料,處理完資料就將其丟棄。
objs = Book.objects.all().iterator()
# iterator()可以一次只從資料庫獲取少量資料,這樣可以節省記憶體
for obj in objs:
print(obj.name)
#BUT,再次遍歷沒有列印,因為迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了
for obj in objs:
print(obj.name)
當然,使用 iterator()
方法來防止生成 cache
,意味著遍歷同一個 QuerySet
時會重複執行查詢。所以使用 iterator()
的時候要當心,確保你的程式碼在操作一個大的 QuerySet
時沒有重複執行查詢。
6. 總結
QuerySet
的 cache
是用於減少程式對資料庫的查詢,在通常的使用下會保證只有在需要的時候才會查詢資料庫。使用 exists()
和 iterator()
方法可以最佳化程式對記憶體的使用。不過,由於它們並不會生成 QuerySet cache
,可能會造成額外的資料庫查詢。