在了解 Django 數(shù)據(jù)庫事務(wù)編程前有必要先了解下數(shù)據(jù)庫事務(wù)。
數(shù)據(jù)庫事務(wù)(transaction)
數(shù)據(jù)庫事務(wù)是對數(shù)據(jù)的某一組修改(insert宽菜、update盼理、delete)操作要么全部執(zhí)行成功辅愿,要么全部執(zhí)行失敗智亮。
數(shù)據(jù)庫事務(wù)有如下特征:
- 原子性(Atomicity):事務(wù)中的全部操作在數(shù)據(jù)庫中是不可分割的,要么全部完成点待,要么全部不執(zhí)行阔蛉。
- 一致性(Consistency):幾個(gè)并行執(zhí)行的事務(wù),其執(zhí)行結(jié)果必須與按某一順序 串行執(zhí)行的結(jié)果相一致癞埠。
- 隔離性(Isolation):事務(wù)的執(zhí)行不受其他事務(wù)的干擾状原,事務(wù)執(zhí)行的中間結(jié)果對其他事務(wù)必須是透明的。
- 持久性(Durability):對于任意已提交事務(wù)燕差,系統(tǒng)必須保證該事務(wù)對數(shù)據(jù)庫的改變不被丟失遭笋,即使數(shù)據(jù)庫出現(xiàn)故障坝冕。
mysql 數(shù)據(jù)庫事務(wù)
以 mysql 為例徒探,其 Innodb 存儲(chǔ)引擎支持事務(wù)。
然后事務(wù)的 SQL 大概為:
begin
some sql ...
...
commit
begin 的作用是開啟一個(gè)事務(wù)( 相當(dāng)于 SET AUTOCOMMIT=0 禁止自動(dòng)提交)喂窟,接下來可能執(zhí)行了幾個(gè) sql 語句测暗,但在 commit 前都還沒有在數(shù)據(jù)庫生效央串,直到 commit 成功后所有的修改才會(huì)生效。
事務(wù)使用注意事項(xiàng)
- 如果沒有顯式的開啟事務(wù)碗啄,每條 SQL 語句都是一個(gè)事務(wù)
- 一般的 select 無需開啟事務(wù)质和,除非是為了 update 執(zhí)行的 select
- 使用事務(wù)必要涉及到鎖,要考慮鎖的影響
Django 數(shù)據(jù)庫事務(wù)
django 里主要使用 transaction (from django.db import transaction )來支持事務(wù)稚字,有以下兩種用法饲宿。
transaction.atomic 裝飾器
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
給 viewfunc 增加 @transaction.atomic 使得此函數(shù)中所有的 insert、update胆描、delete 操作為一組事務(wù)瘫想。
with transaction.atomic
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()
使用 with transaction.atomic 將事務(wù)操作覆蓋,由于此種方式比裝飾器方式能控制更小的范圍昌讲,最小化事務(wù)操作范圍国夜,推薦使用此種方式。
Savepoints
Savepoints 用來設(shè)置一個(gè)保存點(diǎn)短绸,在其后的事務(wù)代碼中(即 transaction.atomic 覆蓋范圍內(nèi))车吹,可以將數(shù)據(jù)庫的修改 回滾(rollback)到保存點(diǎn)的那個(gè)位置。例如:
from django.db import transaction
# open a transaction
@transaction.atomic
def viewfunc(request):
a.save()
# transaction now contains a.save()
sid = transaction.savepoint()
b.save()
# transaction now contains a.save() and b.save()
if want_to_keep_b:
transaction.savepoint_commit(sid)
# open transaction still contains a.save() and b.save()
else:
transaction.savepoint_rollback(sid)
# open transaction now contains only a.save()
transaction.atomic 使用注意事項(xiàng)
- 它只是數(shù)據(jù)庫層面的事務(wù)醋闭,不是python代碼級的事務(wù)窄驹,即它不能保證 python 代碼的并發(fā)性(例如對同一個(gè)全局變量修改)
- 對于只有查詢操作的函數(shù),不需要加 @transaction.atomic
- 對于只會(huì)執(zhí)行一條 SQL 語句的代碼或函數(shù)目尖,不需要加 transaction.atomic馒吴,因?yàn)橐粭l SQL 默認(rèn)就是一個(gè)事務(wù)。
- transaction.atomic 覆蓋的代碼中不要使用 try except 來捕獲 django.models 執(zhí)行的錯(cuò)誤瑟曲,否則會(huì)破壞事務(wù)的目的饮戳。
- transaction.atomic 覆蓋的代碼中不要包含耗時(shí)的操作,比如第三方系統(tǒng)給的網(wǎng)絡(luò)調(diào)用洞拨。因?yàn)槭聞?wù)會(huì)加鎖扯罐,如果網(wǎng)絡(luò)調(diào)用超時(shí),在 timeout 之前鎖不會(huì)釋放烦衣,可能會(huì)報(bào) (1213, 'Deadlock found when trying to get lock; try restarting transaction') 錯(cuò)誤
- 如果在事務(wù)中涉及對 select 后的結(jié)果進(jìn)行修改(例如對某個(gè)字段查詢后參與計(jì)算后再update回去)歹河,請使用 model.objects.select_for_update(),相當(dāng)于對此讀操作也加了鎖花吟。
- 如果 transaction.savepoint() 放在了事務(wù)函數(shù)的第一行秸歧,并且下面也沒有transaction.savepoint(),那么 savepoint 和 savepoint_rollback 都可以去掉衅澈,用默認(rèn)的方式即可键菱。