在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 數(shù)據(jù)庫訪問優(yōu)化
點(diǎn)擊劫持保護(hù)
安全問題歸檔
Model 類參考
將遺留數(shù)據(jù)庫整合到Django
關(guān)聯(lián)對(duì)象參考
內(nèi)建基于類的視圖的API
聚合
Django 中的用戶認(rèn)證
django.contrib.humanize
Django管理文檔生成器
分頁
使用Django輸出CSV
加密簽名
文件儲(chǔ)存API
安全
Django中的測(cè)試
國(guó)際化和本地化
為Django編寫首個(gè)補(bǔ)丁
條件表達(dá)式
日志
模型元選項(xiàng)
部署靜態(tài)文件
執(zhí)行查詢
使用Django認(rèn)證系統(tǒng)
基于類的視圖
中間件
編寫自定義的django-admin命令
Django 的設(shè)置
格式本地化
數(shù)據(jù)庫訪問優(yōu)化
錯(cuò)誤報(bào)告
基于類的內(nèi)建通用視圖
編寫自定義存儲(chǔ)系統(tǒng)
編寫你的第一個(gè) Django 程序 第3部分
編寫數(shù)據(jù)庫遷移
使用表單
編寫你的第一個(gè) Django 程序 第2部分
編寫你的第一個(gè) Django 程序 第1部分
如何使用會(huì)話
系統(tǒng)檢查框架
新手入門
信號(hào)
編寫視圖
如何使用WSGI 部署
編寫你的第一個(gè)Django應(yīng)用,第6部分
常見的網(wǎng)站應(yīng)用工具
Widgets
內(nèi)建的視圖
模型實(shí)例參考
視圖層
Django中的密碼管理
高級(jí)教程:如何編寫可重用的應(yīng)用
國(guó)際化和本地化
"本地特色"附加功能
TemplateResponse 和 SimpleTemplateResponse
模式編輯器
文件上傳
快速安裝指南
部署 Django
表單 API
表單素材 ( <code>Media</code> 類)
管理文件
其它核心功能
查找 API 參考
表單
Admin
數(shù)據(jù)庫函數(shù)
自定義查找
使用基于類的視圖處理表單
管理操作
開發(fā)過程
編寫你的第一個(gè)Django應(yīng)用,第5部分
進(jìn)行原始的sql查詢
模型層
多數(shù)據(jù)庫
編寫你的第一個(gè) Django 程序 第4部分
Django安全
Django 初探
Django異常
重定向應(yīng)用
按需內(nèi)容處理
管理器
視圖裝飾器
驗(yàn)證器
使用Django輸出PDF
File對(duì)象
Django 的快捷函數(shù)
基于類的通用視圖 —— 索引
為模型提供初始數(shù)據(jù)
模板層
URL調(diào)度器
中間件
模型

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

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

性能優(yōu)先

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

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

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

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

...包括:

  • 索引。在你決定哪些索引應(yīng)該添加 之后,這一條具有最高優(yōu)先級(jí)。使用Field.db_index或者M(jìn)eta.index_together在Dhango中添加它們??紤]在你經(jīng)常使用filter()、exclude()、order_by()和其它方法查詢的字段上面添加索引,因?yàn)樗饕兄诩铀俨檎?。注意,設(shè)計(jì)最好的索引方案是一個(gè)復(fù)雜的、數(shù)據(jù)庫相關(guān)的話題,它取決于你應(yīng)用的細(xì)節(jié)。持有索引的副作用可能會(huì)超過查詢速度上的任何收益。
  • 合理使用字段類型。

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

理解查詢集

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

理解查詢集計(jì)算

要避免性能問題,理解以下幾點(diǎn)非常重要:

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

理解緩存屬性

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

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

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

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

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

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

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

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

使用iterator()

當(dāng)你有很多對(duì)象時(shí),QuerySet的緩存行為會(huì)占用大量的內(nèi)存。這種情況下,采用iterator()解決。

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

比如:

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

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

使用QuerySet.extra()

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

使用原始的SQL

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

用唯一的被或索引的列來檢索獨(dú)立對(duì)象

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

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

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

會(huì)快于:

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

因?yàn)閕d被數(shù)據(jù)庫索引,而且是唯一的。

下面這樣做會(huì)十分緩慢:

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

首先, headline沒有被索引,它會(huì)使查詢變得很慢:

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

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

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

使用QuerySet.select_related()和prefetch_related()

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

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

不要獲取你不需要的東西

使用QuerySet.values()和values_list()

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

使用QuerySet.defer()和only()

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

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

使用QuerySet.count()

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

使用QuerySet.exists()

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

但是:

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

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

例如,假設(shè)Email模型有一個(gè)body屬性,并且和User有多對(duì)多的關(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 %}

這是因?yàn)椋?/p>

  • 因?yàn)椴樵兗茄舆t加載的,如果‘display_inbox’為False,不會(huì)查詢數(shù)據(jù)庫。
  • 使用with意味著我們?yōu)榱艘院蟮氖褂?,把user.emails.all儲(chǔ)存在一個(gè)變量中,允許它的緩存被復(fù)用。
  • {% if emails %}的那一行調(diào)用了QuerySet.bool(),它導(dǎo)致user.emails.all()查詢?cè)跀?shù)據(jù)庫上執(zhí)行,并且至少在第一行以一個(gè)ORM對(duì)象的形式返回。如果沒有任何結(jié)果,會(huì)返回False,反之為True。
  • {{ emails|length }}調(diào)用了QuerySet.len()方法,填充了緩存的剩余部分,而且并沒有執(zhí)行另一次查詢。
  • for循環(huán)的迭代器訪問了已經(jīng)緩存的數(shù)據(jù)。

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

使用QuerySet.update()和delete()

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

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

直接使用外鍵的值

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

entry.blog_id

而不是:

entry.blog.id

不要做無謂的排序

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

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

整體插入

創(chuàng)建對(duì)象時(shí),盡可能使用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")

注意該方法有很多注意事項(xiàng),所以確保它適用于你的情況。

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

my_band.members.add(me, my_friend)

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

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

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

{% endraw %}