Django ORM效能優化之count和len方法的選擇(非常詳細推薦乾貨)
接下來我將從原始碼層面分情況和應用分析我們在計算queryset資料集時是用orm的count函式計算長度還是用len函式計算資料集長度。
首先,我們知道ORM查詢queryset資料集是惰性查詢的,只有使用到資料集時,ORM才會真正去執行查詢語句,然後ORM會把查詢到的資料集快取到記憶體中,下次我們使用資料集時是從快取中取值的。這就是ORM的惰性查詢機制和快取機制,還不清楚可以找相應的部落格瞭解其概念,首先理解這兩點我們便能更好地理解接下來的場景及應用。
1.、場景一:我們寫了ORM查詢語句,然後queryset資料集被使用然後快取的情況下,我們使用queryset物件結果集.count()方法時其底層原始碼如下圖是用len()方法計算結果集長度的,所以在此場景有快取的情況下用len()和count()來計算查詢結果集的效果是一樣的;
2、場景二:如果我們只想獲得queryset物件的長度而不做queryset物件的其他操作的情況下,count函式底層實現如下圖用的是資料庫的聚合函式查詢計算結果,
然後取其結果時間複雜度是O(1),空間複雜度為O(1),而len方法底層實現是是需要獲取整個queryset資料集時間複雜度為O(n),並且空間複雜度也為O(n),
這種場景下使用count函式更好;
3、場景三:在初始我們寫了ORM查詢語句,然後接下來我們既要計算查詢後結果集的長度,又要對結果集做其他操作(如獲取每個queryset物件的屬性等),(這裡暫時不考慮分頁時limit減小查詢範圍的情況)。
接下來我們分析len方法和count方法他們分別會做什麼事情,首先如果是len操作的話會先觸發orm的查詢操作得到queryset結果集然後快取,然後後續對結果集的操作直接從快取中取對應的queryset物件,然後是count函式操作因為之前我們講過ORM的惰性查詢機制,在我們執行count函式的時其實這時查詢操作還未真正的執行,也就是此時還沒有queryset結果集的快取,所以此時我們執行count方法會執行一次聚合函式查詢,然後後續我們使用到queryset集合時就會觸發資料庫查詢得到queryset結果集然後快取。
所以在場景三的情況下,使用len()方法計算結果集的長度時會比count方法會更有優勢,因為此時少了一次對資料的聚合查詢操作。
4、場景四:如果是在先執行了資料庫查詢結果集並使用到了queryset結果有了快取的情況下,參考場景一此時用len方法或者count方法的效果是一樣的。
5、場景五:這也是最複雜也比較難判斷的情況,首先步驟一:我們寫了資料庫查詢操作(此時還未使用到這個查詢的結果集),然後步驟二這裡我們可能使用len方法或者count方法來計算結果集的長度,接下來步驟三我們使用分頁元件對結果集進行分頁處理,再步驟四使用我們分頁後的結果集。
這種場景的話對於步驟二我們是使用len方法還是count方法來計算結果集的長度時就需要我們考慮以下幾個因素了,
1)如果此時我們使用的是len方法的話我們會快取整個queryset結果集,並相當於遍歷了整個結果集時間複雜度為O(n),空間複雜度為O(n),步驟三的分頁操作對於我們後端的時間或者空間來說都沒什麼太大幫助了
2)如果此時我們使用的是count()方法來計算長度的話,我們會多一次資料庫查詢操作,但是步驟三的分頁操作相當於查詢結果集時sql語句然後limit 10(數字取決於我們分頁的size),這樣我們的時間複雜度和空間複雜度都可以降到O(1)
個人傾向的話可能在此種情況我會使用count方法,通過多一次資料庫查詢操作來降低時間和空間的複雜度還是挺划算的。