Django數據層提供各類途徑優化數據的訪問,一個項目大量優化工做通常是放在後期來作,早期的優化是「萬惡之源」,這是前人總結的經驗,不無道理。若是事先理解Django的優化技巧,開發過程當中稍稍留意,後期會省很多的工做量。
一 利用標準數據庫優化技術:
傳統數據庫優化技術博大精深,不一樣的數據庫有不一樣的優化技巧,但重心仍是有規則的。在這裏算是題外話,挑兩點通用的說說:
索引,給關鍵的字段添加索引,性能能更上一層樓,如給表的關聯字段,搜索頻率高的字段加上索引等。Django創建實體的時候,支持給字段添加索引,具體參考Django.db.models.Field.db_index。按照經驗,Django創建實體以前應該早想好表的結構,儘可能想到後面的擴展性,避免後面的表的結構變得面目全非。
使用適當字段類型,原本varchar就搞定的字段,就別要text類型,小細節別不關緊要,後頭數據量一上去,幾億幾億的數據,小字段極可能是大問題。
二 瞭解Django的QuerySets:
瞭解Django的QuerySets對象,對優化簡單程序有相當重要的做用。QuerySets是有緩存的,一旦取出來,它就會在內存裏呆上一段時間,儘可能重用它。舉個簡單的例子:
瞭解緩存屬性:
>>> entry = Entry.objects.get(id=1)
>>> entry.blog # 博客實體第一次取出,是要訪問數據庫的
>>> entry.blog # 第二次再用,那它就是緩存裏的實體了,再也不訪問數據庫
但下面的例子就不同,
>>> entry = Entry.objects.get(id=1) python
>>> entry.authors.all() # 第一次all函數會查詢數據庫 數據庫
>>> entry.authors.all() # 第二次all函數還會查詢數據庫 緩存
all,count exists是調用函數(須要鏈接數據庫處理結果的),注意在模板template裏的代碼,模板裏不容許括號,但若是使用此類的調用函數,同樣去鏈接數據庫的,能用緩存的數據就別鏈接到數據庫去處理結果。還要注意的是,自定義的實體屬性,若是調用函數的,記得本身加上緩存策略。
利用好模板的with標籤:
模板中屢次使用的變量,要用with標籤,把它當作變量的緩存行爲吧。
使用QuerySets的iterator():
一般QuerySets先調用iterator再緩存起來,當獲取大量的實體列表而僅使用一次時,緩存行爲會耗費寶貴的內存,這時iterator()能幫到你,iterator()只調用iterator而省去了緩存步驟,顯著減小內存佔用率,具體參考相關文檔。
三 數據庫的工做就交給數據庫自己計算,別用Python處理: 函數
1 使用 filter and exclude 過濾不須要的記錄,這兩個是最經常使用語句,至關是SQL的where。 性能
2 同一實體裏使用F()表達式過濾其餘字段。 fetch
3 使用annotate對數據庫作聚合運算。
不要用python語言對以上類型數據過濾篩選,一樣的結果,python處理複雜度要高,並且效率不高, 白白浪費內存。
使用QuerySet.extra():
extra雖然擴展性不太好,但功能很強大,若是實體裏須要須要增長額外屬性,不得已時,經過extra來實現,也是個好辦法。
使用原生的SQL語句:
若是發現Django的ORM已經實現不了你的需求,而extra也無濟於事的時候,那就用原生SQL語句吧,用Djangoango.db.connection.queries去實現你須要的東西。
四 若是須要就一次性取出你所須要的數據:
單一動做(如:同一個頁面)須要屢次鏈接數據庫時,最好一次性取出全部須要的數據,減小鏈接數據庫次數。此類需求推薦使用QuerySet.select_related() 和 prefetch_related()。
相反,別取出你不須要的東西,模版templates裏每每只須要實體的某幾個字段而不是所有,這時QuerySet.values() 和 values_list(),對你有用,它們只取你須要的字段,返回字典dict和列表list類型的東西,在模版裏夠用便可,這可減小內存損耗,提升性能。
一樣QuerySet.defer()和only()對提升性能也有很大的幫助,一個實體裏可能有很多的字段,有些字段包含不少元數據,好比博客的正文,不少字符組成,Django獲取實體時(取出實體過程當中會進行一些python類型轉換工做),咱們能夠延遲大量元數據字段的處理,只處理須要的關鍵字段,這時QuerySet.defer()就派上用場了,在函數裏傳入須要延時處理的字段便可;而only()和defer()是相反功能。
使用QuerySet.count()代替len(queryset),雖然這兩個處理得出的結果是同樣的,但前者性能優秀不少。同理判斷記錄存在時,QuerySet.exists()比if queryset實在強得太多了。
固然同樣的結果,在緩存裏已經存在,就別濫用count(),exists(),all()函數了。
五 懂減小數據庫的鏈接數:
使用 QuerySet.update() 和 delete(),這兩個函數是能批處理多條記錄的,適當使用它們事半功倍;若是能夠,別一條條數據去update delete處理。
對於一次性取出來的關聯記錄,獲取外鍵的時候,直接取關聯表的屬性,而不是取關聯屬性,如: 優化
entry.blog.id spa
優於 對象
entry.blog_id blog
善於使用批量插入記錄,如:
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")
前者只鏈接一次數據庫,然後者鏈接兩次哦。
還有類似的動做須要注意的,如:多對多的關係,
my_band.members.add(me, my_friend)
優於
my_band.members.add(me)
my_band.members.add(my_friend)