django 1.8 官方文檔翻譯: 2-6-4 數(shù)據(jù)庫訪問優(yōu)化

Django 文檔協(xié)作翻譯小組人手緊缺浓领,有興趣的朋友可以加入我們陶耍,完全公益性質(zhì)奋蔚。

交流群:467338606

網(wǎng)站:http://python.usyiyi.cn/django/index.html

數(shù)據(jù)庫訪問優(yōu)化

Django的數(shù)據(jù)庫層提供了很多方法來幫助開發(fā)者充分的利用他們的數(shù)據(jù)庫她混。這篇文檔收集了相關(guān)文檔的一些鏈接烈钞,添加了大量提示,并且按照優(yōu)化數(shù)據(jù)庫使用的步驟的概要來組織坤按。

性能優(yōu)先

作為通用的編程實踐毯欣,性能的重要性不用多說。弄清楚你在執(zhí)行什么查詢以及你的開銷花在哪里臭脓。你也可能想使用外部的項目酗钞,像django-debug-toolbar,或者直接監(jiān)控數(shù)據(jù)庫的工具来累。

記住你可以優(yōu)化速度砚作、內(nèi)存占用,甚至二者一起嘹锁,這取決于你的需求葫录。一些針對其中一個的優(yōu)化會對另一個不利,但有時會對二者都有幫助领猾。另外米同,數(shù)據(jù)庫進(jìn)程做的工作,可能和你在Python代碼中做的相同工作不具有相同的開銷摔竿。決定你的優(yōu)先級是什么面粮,是你自己的事情,你必須要權(quán)衡利弊继低,按需使用它們熬苍,因為這取決于你的應(yīng)用和服務(wù)器。

對于下面提到的任何事情袁翁,要記住在任何修改后驗證一下柴底,確保修改是有利的,并且足夠有利梦裂,能超過你代碼中可讀性的下降似枕。下面的所有建議都帶有警告,在你的環(huán)境中大體原則可能并不適用年柠,或者會起到相反的效果凿歼。

使用標(biāo)準(zhǔn)數(shù)據(jù)庫優(yōu)化技巧

...包括:

  • 索引褪迟。在你決定哪些索引應(yīng)該添加 之后,這一條具有最高優(yōu)先級答憔。使用Field.db_index或者M(jìn)eta.index_together在Dhango中添加它們味赃。考慮在你經(jīng)常使用filter()虐拓、exclude()心俗、order_by()和其它方法查詢的字段上面添加索引,因為索引有助于加速查找蓉驹。注意城榛,設(shè)計最好的索引方案是一個復(fù)雜的、數(shù)據(jù)庫相關(guān)的話題态兴,它取決于你應(yīng)用的細(xì)節(jié)狠持。持有索引的副作用可能會超過查詢速度上的任何收益。
  • 合理使用字段類型瞻润。

我們假設(shè)你已經(jīng)完成了上面這些顯而易見的事情喘垂。這篇文檔剩下的部分,著重于講解如何以不做無用功的方式使用Django绍撞。這篇文檔也沒有強(qiáng)調(diào)用在開銷大的操作上其它的優(yōu)化技巧正勒,像general purpose caching。

理解查詢集

理解查詢集(QuerySets) 是通過簡單的代碼獲取較好性能至關(guān)重要的一步傻铣。特別是:

理解查詢集計算

要避免性能問題章贞,理解以下幾點非常重要:

  • QuerySets是延遲的。
  • 什么時候它們被計算出來矾柜。
  • 數(shù)據(jù)在內(nèi)存中如何存儲阱驾。

理解緩存屬性

和整個QuerySet的緩存相同,ORM對象的屬性的結(jié)果中也存在緩存怪蔑。通常來說里覆,不可調(diào)用的屬性會被緩存。例如下面的博客模型示例:

>>> entry = Entry.objects.get(id=1)
>>> entry.blog   # Blog object is retrieved at this point
>>> entry.blog   # cached version, no DB access

但是通常來講缆瓣,可調(diào)用的屬性每一次都會訪問數(shù)據(jù)庫喧枷。

>>> entry = Entry.objects.get(id=1)
>>> entry.authors.all()   # query performed
>>> entry.authors.all()   # query performed again

要小心當(dāng)你閱讀模板代碼的時候 —— 模板系統(tǒng)不允許使用圓括號,但是會自動調(diào)用callable對象弓坞,會隱藏上述區(qū)別隧甚。

要小心使用你自定義的屬性 —— 實現(xiàn)所需的緩存取決于你,例如使用cached_property裝飾符渡冻。

使用with模板標(biāo)簽

要利用QuerySet的緩存行為戚扳,你或許需要使用with模板標(biāo)簽。

使用iterator()

當(dāng)你有很多對象時族吻,QuerySet的緩存行為會占用大量的內(nèi)存获搏。這種情況下,采用iterator()解決侍芝。

在數(shù)據(jù)庫中而不是Python中做數(shù)據(jù)庫的工作

比如:

  • 在最基礎(chǔ)的層面上,使用過濾器和反向過濾器對數(shù)據(jù)庫進(jìn)行過濾茧泪。
  • 使用F 表達(dá)式在相同模型中基于其他字段進(jìn)行過濾。
  • 使用數(shù)據(jù)庫中的注解和聚合。

如果上面那些都不夠用,你可以自己生成SQL語句:

使用QuerySet.extra()

extra()是一個移植性更差凝垛,但是功能更強(qiáng)的方法,它允許一些SQL語句顯式添加到查詢中蜓谋。如果這些還不夠強(qiáng)大:

使用原始的SQL

編寫你自己的自定義SQL語句梦皮,來獲取數(shù)據(jù)或者填充模型。使用django.db.connection.queries來了解Django為你編寫了什么孤澎,以及從這里開始届氢。

用唯一的被或索引的列來檢索獨立對象

有兩個原因在get()中欠窒,用帶有unique或者db_index的列檢索獨立對象覆旭。首先,由于查詢經(jīng)過了數(shù)據(jù)庫的索引岖妄,所以會更快型将。其次,如果很多對象匹配查詢荐虐,查詢會更慢一些七兜;列上的唯一性約束確保這種情況永遠(yuǎn)不會發(fā)生。

所以福扬,使用博客模型的例子:

>>> entry = Entry.objects.get(id=10)

會快于:

>>> entry = Entry.object.get(headline="News Item Title")

因為id被數(shù)據(jù)庫索引腕铸,而且是唯一的。

下面這樣做會十分緩慢:

>>> entry = Entry.objects.get(headline__startswith="News")

首先铛碑, headline沒有被索引狠裹,它會使查詢變得很慢:

其次,這次查找并不確保返回唯一的對象汽烦。如果查詢匹配到多于一個對象涛菠,它會在數(shù)據(jù)庫中遍歷和檢索所有這些對象。如果記錄中返回了成百上千個對象撇吞,代價是非常大的俗冻。如果數(shù)據(jù)庫運行在分布式服務(wù)器上,網(wǎng)絡(luò)開銷和延遲也是一大因素牍颈,代價會是它們的組合迄薄。

一次性檢索你需要的任何東西

在不同的位置多次訪問數(shù)據(jù)庫,一次獲取一個數(shù)據(jù)集煮岁,通常來說不如在一次查詢中獲取它們更高效讥蔽。如果你在一個循環(huán)中執(zhí)行查詢死姚,這尤其重要。有可能你會做很多次數(shù)據(jù)庫查詢勤篮,但只需要一次就夠了都毒。所以:

使用QuerySet.select_related()和prefetch_related()

充分了解并使用select_related()和prefetch_related():

  • 在視圖的代碼中,
  • 以及在適當(dāng)?shù)墓芾砥骱湍J(rèn)管理器中碰缔。要意識到你的管理器什么時候被使用和不被使用账劲;有時這很復(fù)雜,所以不要有任何假設(shè)金抡。

不要獲取你不需要的東西

使用QuerySet.values()和values_list()

當(dāng)你僅僅想要一個帶有值的字典或者列表瀑焦,并不需要使用ORM模型對象時,可以適當(dāng)使用values()梗肝。對于在模板代碼中替換模型對象榛瓮,這樣會非常有用 —— 只要字典中帶有的屬性和模板中使用的一致,就沒問題巫击。

使用QuerySet.defer()和only()

如果一些數(shù)據(jù)庫的列你并不需要(或者大多數(shù)情況下并不需要)禀晓,使用defer()和only()來避免加載它們。注意如果你確實要用到它們坝锰,ORM會在另外的查詢之中獲取它們粹懒。如果你不能夠合理地使用這些函數(shù),不如不用顷级。

另外凫乖,當(dāng)建立起一個帶有延遲字段的模型時,要意識到一些(小的弓颈、額外的)消耗會在Django內(nèi)部產(chǎn)生帽芽。不要不分析數(shù)據(jù)庫就盲目使用延遲字段,因為數(shù)據(jù)庫必須從磁盤中讀取大多數(shù)非text和VARCHAR數(shù)據(jù)翔冀,在結(jié)果中作為單獨的一行导街,即使其中的列很少。 defer()和only()方法在你可以避免加載大量文本數(shù)據(jù)橘蜜,或者可能要花大量時間處理而返回給Python的字段時菊匿,特別有幫助。像往常一樣计福,應(yīng)該先寫出個大概跌捆,之后再優(yōu)化。

使用QuerySet.count()

...如果你想要獲取大小象颖,不要使用 len(queryset)佩厚。

使用QuerySet.exists()

...如果你想要知道是否存在至少一個結(jié)果,不要使用if queryset说订。

但是:

不要過度使用 count() 和 exists()

如果你需要查詢集中的其他數(shù)據(jù)抄瓦,就把它加載出來潮瓶。

例如,假設(shè)Email模型有一個body屬性钙姊,并且和User有多對多的關(guān)聯(lián)毯辅,下面的的模板代碼是最優(yōu)的:

{% if display_inbox %}
  {% with emails=user.emails.all %}
    {% if emails %}
      <p>You have {{ emails|length }} email(s)</p>
      {% for email in emails %}
        <p>{{ email.body }}</p>
      {% endfor %}
    {% else %}
      <p>No messages today.</p>
    {% endif %}
  {% endwith %}
{% endif %}

這是因為:

  • 因為查詢集是延遲加載的,如果‘display_inbox’為False煞额,不會查詢數(shù)據(jù)庫思恐。
  • 使用with意味著我們?yōu)榱艘院蟮氖褂茫製ser.emails.all儲存在一個變量中膊毁,允許它的緩存被復(fù)用胀莹。
  • {% if emails %}的那一行調(diào)用了QuerySet.bool(),它導(dǎo)致user.emails.all()查詢在數(shù)據(jù)庫上執(zhí)行婚温,并且至少在第一行以一個ORM對象的形式返回描焰。如果沒有任何結(jié)果,會返回False栅螟,反之為True荆秦。
  • {{ emails|length }}調(diào)用了QuerySet.len()方法,填充了緩存的剩余部分嵌巷,而且并沒有執(zhí)行另一次查詢萄凤。
  • for循環(huán)的迭代器訪問了已經(jīng)緩存的數(shù)據(jù)。

總之搪哪,這段代碼做了零或一次查詢。唯一一個慎重的優(yōu)化就是with標(biāo)簽的使用坪圾。在任何位置使用QuerySet.exists()或者QuerySet.count()都會導(dǎo)致額外的查詢晓折。

使用QuerySet.update()和delete()

通過QuerySet.update()使用批量的SQL UPDATE語句,而不是獲取大量對象兽泄,設(shè)置一些值再單獨保存漓概。與此相似,在可能的地方使用批量deletes病梢。

但是要注意胃珍,這些批量的更新方法不會在單獨的實例上面調(diào)用save()或者delete()方法,意思是任何你向這些方法添加的自定義行為都不會被執(zhí)行蜓陌,包括由普通數(shù)據(jù)庫對象的信號驅(qū)動的任何方法觅彰。

直接使用外鍵的值

如果你僅僅需要外鍵當(dāng)中的一個值,要使用對象上你已經(jīng)取得的外鍵的值钮热,而不是獲取整個關(guān)聯(lián)對象再得到它的主鍵填抬。例如,執(zhí)行:

entry.blog_id

而不是:

entry.blog.id

不要做無謂的排序

排序并不是沒有代價的隧期;每個需要排序的字段都是數(shù)據(jù)庫必須執(zhí)行的操作飒责。如果一個模型具有默認(rèn)的順序(Meta.ordering)赘娄,并且你并不需要它,通過在查詢集上無參調(diào)用order_by() 來移除它宏蛉。

向你的數(shù)據(jù)庫添加索引可能有助于提升排序性能遣臼。

整體插入

創(chuàng)建對象時,盡可能使用bulk_create()來減少SQL查詢的數(shù)量拾并。例如:

Entry.objects.bulk_create([
    Entry(headline="Python 3.0 Released"),
    Entry(headline="Python 3.1 Planned")
])

...更優(yōu)于:

Entry.objects.create(headline="Python 3.0 Released")
Entry.objects.create(headline="Python 3.1 Planned")

注意該方法有很多注意事項暑诸,所以確保它適用于你的情況。

這也可以用在ManyToManyFields中辟灰,所以:

my_band.members.add(me, my_friend)

...更優(yōu)于:

my_band.members.add(me)
my_band.members.add(my_friend)

...其中Bands和Artists具有多對多關(guān)聯(lián)个榕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市芥喇,隨后出現(xiàn)的幾起案子西采,更是在濱河造成了極大的恐慌,老刑警劉巖继控,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件械馆,死亡現(xiàn)場離奇詭異,居然都是意外死亡武通,警方通過查閱死者的電腦和手機(jī)霹崎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冶忱,“玉大人尾菇,你說我怎么就攤上這事∏羟梗” “怎么了派诬?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長链沼。 經(jīng)常有香客問我默赂,道長,這世上最難降的妖魔是什么括勺? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任缆八,我火速辦了婚禮,結(jié)果婚禮上疾捍,老公的妹妹穿的比我還像新娘奈辰。我一直安慰自己,他們只是感情好拾氓,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布冯挎。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪房官。 梳的紋絲不亂的頭發(fā)上趾徽,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音翰守,去河邊找鬼孵奶。 笑死,一個胖子當(dāng)著我的面吹牛蜡峰,可吹牛的內(nèi)容都是我干的了袁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼湿颅,長吁一口氣:“原來是場噩夢啊……” “哼载绿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起油航,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤崭庸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谊囚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怕享,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年镰踏,在試婚紗的時候發(fā)現(xiàn)自己被綠了函筋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡奠伪,死狀恐怖跌帐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芳来,我是刑警寧澤含末,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站即舌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挎袜。R本人自食惡果不足惜顽聂,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盯仪。 院中可真熱鬧紊搪,春花似錦、人聲如沸全景。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爸黄。三九已至滞伟,卻和暖如春揭鳞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梆奈。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工野崇, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亩钟。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓乓梨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親清酥。 傳聞我的和親對象是個殘疾皇子扶镀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容