1. 程式人生 > >Django model 進階以及優化

Django model 進階以及優化

獲取 由於 結果 高效 代碼 重復 literal hits next

一、QuerySet

可叠代

querysey=models.Book.objects.all()
for book in querysey:
    print(book.title)

可切片

Book.objects.all()[:3]
Book.objects.all()[3:6]
# 不支持負的索引,例如Book.objects.all()[0:-1]。通常,查詢集的切片返回一個新的查詢集,它不會執行查詢。

惰性查詢

QuerySet 是懶惰的 -- 創建查詢集不會帶來任何數據庫的訪問;直到查詢集需要求值時,Django 才會真正運行這個查詢。

queryset = models.Book.objects.all()  #
not hits database print(queryset) # hits database for book in queryset: print(book.title) # Hits database

緩存機制

每個查詢集都包含一個緩存來最小化對數據庫的訪問。理解它是如何工作的將讓你編寫最高效的代碼。

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

的求值將重用緩存的結果。

請牢記這個緩存行為,因為對查詢集使用不當的話,它會坑你的。例如,下面的語句創建兩個查詢集,對它們求值,然後扔掉它們:

print(book.title for book in models.Book.objects.all())  # hits database
print(book.price for book in models.Book.objects.all())  # hits database

這意味著相同的數據庫查詢將執行兩次,顯然倍增了你的數據庫負載。同時,還有可能兩個結果列表並不包含相同的數據庫記錄,因為在兩次請求期間有可能有 book 被添加進來或刪除掉。為了避免這個問題,只需保存查詢集

並重新使用它:

queryset = models.Book.objects.all()
print(book.title for book in queryset)
print(book.price for book in queryset)
# 下面也是一次數據庫查詢
for book in querysey:
    print(book.title)
    print(book.price)

何時查詢集不會被緩存?

當只對查詢集的部分進行求值時會檢查緩存, 如果這個部分不在緩存中,那麽接下來查詢返回的記錄都將不會被緩存。所以,這意味著使用切片或索引來限制查詢集將不會填充緩存。

例如,重復獲取查詢集對象中一個特定的索引將每次都查詢數據庫:

queryset = models.Book.objects.all()
print(queryset[1])  # hits database
print(queryset[1])  # hits database

然而,如果已經對全部查詢集求值過,則將檢查緩存:

queryset = models.Book.objects.all()
[book for book in queryset]  # hits database
print(queryset[1])           # use cache
print(queryset[1])           # use cache

簡單地打印查詢集不會填充緩存:

queryset = models.Book.objects.all()
print(queryset)  # hits database
print(queryset)  # hits database

iterator()

當queryset非常巨大時,cache會成為問題。

處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統進程,讓程序瀕臨崩潰。要避免在遍歷數據的同時產生 queryset cache,可以使用 iterator() 方法來獲取數據,處理完數據就將其丟棄。

# iterator() 可以一次只從數據庫獲取少量數據,這樣可以節省內存
objs = models.Book.objects.all().iterator()
for obj in objs:
    print(obj.title)
# 註意,再次遍歷沒有打印,因為叠代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了
for obj in objs:
    print(obj.title)

當然,使用 iterator() 方法來防止生成cache,意味著遍歷同一個 queryset 時會重復執行查詢。所以使用 iterator() 的時候要當心,確保你的代碼在操作一個大的 queryset 時沒有重復執行查詢。

總結

queryset 的 cache 是用於減少程序對數據庫的查詢,在通常的使用下會保證只有在需要的時候才會查詢數據庫。 使用 exists() 和 iterator() 方法可以優化程序對內存的使用。不過,由於它們並不會生成queryset cache,可能會造成額外的數據庫查詢。

1

Django model 進階以及優化