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

2022-03-16 18:03 更新

Django默認(rèn)的事務(wù)行為

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

將事務(wù)綁定到HTTP請(qǐng)求

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

注意:雖然這種簡(jiǎn)潔的事務(wù)模型很吸引人,但在流量增加時(shí),也會(huì)降低效率。為每個(gè)視圖打開(kāi)一個(gè)事務(wù)都會(huì)帶來(lái)一些開(kāi)銷(xiāo)。對(duì)性能的影響程度取決于應(yīng)用執(zhí)行的查詢(xún)語(yǔ)句和數(shù)據(jù)庫(kù)處理鎖的能力。

每次請(qǐng)求的事務(wù)和流式響應(yīng)

當(dāng)視圖返回一個(gè) ?StreamingHttpResponse ?時(shí),獲取該響應(yīng)的內(nèi)容總會(huì)執(zhí)行代碼,生成內(nèi)容。由于早就返回了該視圖,某些代碼會(huì)在事務(wù)外執(zhí)行。
一般來(lái)說(shuō),不建議在生成流式響應(yīng)時(shí)寫(xiě)入數(shù)據(jù)庫(kù),因?yàn)樵陂_(kāi)始發(fā)送響應(yīng)后,就沒(méi)有能有效處理錯(cuò)誤的方法了。

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

non_atomic_requests(using=None)

該裝飾器會(huì)為指定視圖取消 ?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()

只有在它被應(yīng)用到視圖時(shí)才會(huì)生效。

顯式控制事務(wù)

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

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

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

確保原子塊始終是最外層的原子塊有時(shí)很有用,確保在退出塊時(shí)提交任何數(shù)據(jù)庫(kù)更改而沒(méi)有錯(cuò)誤。 這稱(chēng)為持久性,可以通過(guò)設(shè)置?durable=True?來(lái)實(shí)現(xiàn)。 如果原子塊嵌套在另一個(gè)塊中,則會(huì)引發(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 ?來(lái)允許自然處理完整性錯(cuò)誤:

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()

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

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

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

當(dāng)回滾事務(wù)時(shí),你可能需要手工恢復(fù)模型狀態(tài)。

當(dāng)事務(wù)回滾時(shí),模型字段的值不會(huì)被恢復(fù)。除非你手工恢復(fù)初始的字段值,否則這會(huì)導(dǎo)致模型狀態(tài)不一致。
例如,給定帶有 ?active ?字段的 ?MyModel ?模型,如果在事務(wù)中更新 ?active ?到 ?True ?失敗,那么這個(gè)片段確保最后的 ?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ù)庫(kù)連接的自動(dòng)提交狀態(tài)將引發(fā)異常。
?atomic ?帶有 ?using ?參數(shù),這個(gè)參數(shù)是數(shù)據(jù)庫(kù)名字。如果這個(gè)參數(shù)沒(méi)有提供,Django 會(huì)使用默認(rèn)數(shù)據(jù)庫(kù)。
在后臺(tái),Django 的事務(wù)管理代碼:

  • 當(dāng)進(jìn)入最外面的 ?atomic塊時(shí)打開(kāi)事務(wù);
  • 當(dāng)進(jìn)入 ?atomic ?塊內(nèi)部時(shí)創(chuàng)建一個(gè)保存點(diǎn);
  • 從塊內(nèi)部退出時(shí)釋放或回滾保存點(diǎn);
  • 離開(kāi)塊的最外層時(shí)提交或回滾事務(wù)。

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

當(dāng)自動(dòng)提交關(guān)閉時(shí),可以使用 ?atomic ?。它將只使用保存點(diǎn),即使對(duì)于最外面的塊也是如此。

性能考慮因素

打開(kāi)事務(wù)會(huì)對(duì)數(shù)據(jù)庫(kù)服務(wù)器有性能成本。盡量減少這種開(kāi)銷(xiāo),要保持事務(wù)盡可能簡(jiǎn)短。如果正在 Django 的請(qǐng)求 / 響應(yīng)周期之外,在長(zhǎng)時(shí)間運(yùn)行的進(jìn)程中使用 ?atomic()? ,這點(diǎn)尤其重要。


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)