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

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

數(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ù)庫進程做的工作,可能和你在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或者Meta.index_together在Dhango中添加它們??紤]在你經(jīng)常使用filter()、exclude()、order_by()和其它方法查詢的字段上面添加索引,因為索引有助于加速查找。注意,設(shè)計最好的索引方案是一個復(fù)雜的、數(shù)據(jù)庫相關(guān)的話題,它取決于你應(yīng)用的細(xì)節(jié)。持有索引的副作用可能會超過查詢速度上的任何收益。
  • 合理使用字段類型。

我們假設(shè)你已經(jīng)完成了上面這些顯而易見的事情。這篇文檔剩下的部分,著重于講解如何以不做無用功的方式使用Django。這篇文檔也沒有強調(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ù)庫進行過濾。
  • 使用F 表達(dá)式在相同模型中基于其他字段進行過濾。
  • 使用數(shù)據(jù)庫中的注解和聚合。

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

使用QuerySet.extra()

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

使用原始的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)。

{% endraw %}