Django4.0 數(shù)據(jù)庫事務-管理數(shù)據(jù)庫事務

2022-03-16 18:03 更新

Django默認的事務行為

Django 默認的事務行為是自動提交。除非事務正在執(zhí)行,每個查詢將會馬上自動提交到數(shù)據(jù)庫。
Django 自動使用事務或還原點,以確保需多次查詢的 ORM 操作的一致性,特別是 ?delete()? 和 ?update()? 操作。
由于性能原因,Django 的 ?TestCase? 類同樣將每個測試用事務封裝起來。

將事務綁定到HTTP請求

在 Web 里,處理事務比較常用的方式是將每個請求封裝在一個事務中。 在你想啟用該行為的數(shù)據(jù)庫中,把配置中的參數(shù) ?ATOMIC_REQUESTS ?設置為 ?True?。
它是這樣工作的:在調(diào)用視圖方法前,Django 先生成一個事務。如果響應能正常生成,Django 會提交該事務。而如果視圖出現(xiàn)異常,Django 則會回滾該事務。
你可以在你的視圖代碼中使用還原點執(zhí)行子事務,一般會使用 ?atomic()? 上下文管理器。但是,在視圖結(jié)束時,要么所有的更改都被提交,要么所有的更改都不被提交。

注意:雖然這種簡潔的事務模型很吸引人,但在流量增加時,也會降低效率。為每個視圖打開一個事務都會帶來一些開銷。對性能的影響程度取決于應用執(zhí)行的查詢語句和數(shù)據(jù)庫處理鎖的能力。

每次請求的事務和流式響應

當視圖返回一個 ?StreamingHttpResponse ?時,獲取該響應的內(nèi)容總會執(zhí)行代碼,生成內(nèi)容。由于早就返回了該視圖,某些代碼會在事務外執(zhí)行。
一般來說,不建議在生成流式響應時寫入數(shù)據(jù)庫,因為在開始發(fā)送響應后,就沒有能有效處理錯誤的方法了。

實際上,此功能只是簡單地用下文介紹的 ?atomic()? 裝飾器裝飾了每個視圖函數(shù)。
注意,只有視圖被限制在事務中執(zhí)行。中間件在事務之外運行,同理,渲染模板響應也是在事務之外運行的。
即便啟用了 ?ATOMIC_REQUESTS?,仍能避免視圖在事務中運行。

non_atomic_requests(using=None)

該裝飾器會為指定視圖取消 ?ATOMIC_REQUESTS ?的影響。

from django.db import transaction

@transaction.non_atomic_requests
def my_view(request):
    do_stuff()

@transaction.non_atomic_requests(using='other')
def my_other_view(request):
    do_stuff_on_the_other_database()

只有在它被應用到視圖時才會生效。

顯式控制事務

Django 提供了一個 API 控制數(shù)據(jù)庫事務。

atomic(using=None, savepoint=True, durable=False)

原子性是數(shù)據(jù)庫事務的定義屬性。 ?atomic ?允許創(chuàng)建代碼塊來保證數(shù)據(jù)庫的原子性。如果代碼塊成功創(chuàng)建,這個變動會提交到數(shù)據(jù)庫。如果有異常,變動會回滾。
?atomic ?塊可以嵌套。在這個例子里,當內(nèi)部塊成功完成時,如果在稍后外部塊里引發(fā)了異常,則仍可回滾到最初效果。

確保原子塊始終是最外層的原子塊有時很有用,確保在退出塊時提交任何數(shù)據(jù)庫更改而沒有錯誤。 這稱為持久性,可以通過設置?durable=True?來實現(xiàn)。 如果原子塊嵌套在另一個塊中,則會引發(fā) ?RuntimeError?。

atomic 既可用作裝飾器:

from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

并作為上下文管理器:

from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()

    with transaction.atomic():
        # This code executes inside a transaction.
        do_more_stuff()

在 ?try?/?except ?塊中使用裝飾器 ?atomic ?來允許自然處理完整性錯誤:

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()

在這個例子里,雖然 ?generate_relationships()? 會通過破壞完整性約束導致數(shù)據(jù)庫錯誤,但你可以 ?add_children()? 中執(zhí)行查找,來自 ?create_parent()? 的變化也會在這里,并且綁定到相同的事務。注意,任何試圖在 ?generate_relationships()? 中執(zhí)行的操作在 ?handle_exception()? 被調(diào)用的時候也會安全的回滾,因此異常處理也會在必要的時候在數(shù)據(jù)庫上操作。

要避免在 atomic 內(nèi)部捕捉異常!

當存在 ?atomic ?塊時, Django 查看它是否正常退出或存在異常來決定是提交還是正常回滾。如果你在 ?atomic ?內(nèi)部捕捉并且處理異常,你可以對 Django 隱藏問題代碼。這會導致一些意外的行為。
這主要是 ?DatabaseError ?和它的子類的一個問題(比如 ?IntegrityError ?)。出現(xiàn)這樣的錯誤之后,事務會奔潰,并且 Django 將在 ?atomic ?塊的末尾執(zhí)行回滾。如果你打算在回滾發(fā)生的時候運行數(shù)據(jù)庫查詢,Django 將引發(fā) ?TransactionManagementError ?錯誤。當 ORM 相關的信號處理程序引發(fā)異常時,你也可能遇到這個問題。
捕捉數(shù)據(jù)庫錯誤的正確的方法是像上方所示那樣圍繞 ?atomic ?塊。如有需要,為此目的可以添加額外的 ?atomic ?塊。這個模式有別的優(yōu)勢:如果異常發(fā)生,它會明確界定哪些操作將回滾。
如果捕獲由原始SQL查詢引發(fā)的異常,那么Django的行為是未指定的,并且依賴于數(shù)據(jù)庫。

當回滾事務時,你可能需要手工恢復模型狀態(tài)。

當事務回滾時,模型字段的值不會被恢復。除非你手工恢復初始的字段值,否則這會導致模型狀態(tài)不一致。
例如,給定帶有 ?active ?字段的 ?MyModel ?模型,如果在事務中更新 ?active ?到 ?True ?失敗,那么這個片段確保最后的 ?if obj.active? 檢查使用正確的值:

from django.db import DatabaseError, transaction

obj = MyModel(active=False)
obj.active = True
try:
    with transaction.atomic():
        obj.save()
except DatabaseError:
    obj.active = False

if obj.active:
    ...

為了保證原子性,?atomic ?禁用了一些API。在 ?atomic ?塊中試圖提交、回滾或改變數(shù)據(jù)庫連接的自動提交狀態(tài)將引發(fā)異常。
?atomic ?帶有 ?using ?參數(shù),這個參數(shù)是數(shù)據(jù)庫名字。如果這個參數(shù)沒有提供,Django 會使用默認數(shù)據(jù)庫。
在后臺,Django 的事務管理代碼:

  • 當進入最外面的 ?atomic塊時打開事務;
  • 當進入 ?atomic ?塊內(nèi)部時創(chuàng)建一個保存點;
  • 從塊內(nèi)部退出時釋放或回滾保存點;
  • 離開塊的最外層時提交或回滾事務。

你可以通過設置 ?savepoint ?參數(shù)為 ?False ?來為內(nèi)部塊禁用保存點的創(chuàng)建。如果發(fā)生異常,Django將在退出帶有保存點的第一個父塊(如果有的話)時執(zhí)行回滾,否則退出最外面的塊。外部事物仍保證了原子性。僅當保存點開銷明顯時,才應使用此選項。它的缺點是破壞了上述錯誤處理。

當自動提交關閉時,可以使用 ?atomic ?。它將只使用保存點,即使對于最外面的塊也是如此。

性能考慮因素

打開事務會對數(shù)據(jù)庫服務器有性能成本。盡量減少這種開銷,要保持事務盡可能簡短。如果正在 Django 的請求 / 響應周期之外,在長時間運行的進程中使用 ?atomic()? ,這點尤其重要。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號