資料庫的讀寫操作中,事務在保證數據的安全性和一致性方面起著關鍵的作用,而回滾正是這裡面的核心操作。Django的ORM在事務方面也提供了不少的API。有事務出錯的整體回滾操作,也有基於保存點的部分回滾。本文將討論Django中的這兩種機制的運行原理。 Django利用django.db.transa ...
資料庫的讀寫操作中,事務在保證數據的安全性和一致性方面起著關鍵的作用,而回滾正是這裡面的核心操作。Django的ORM在事務方面也提供了不少的API。有事務出錯的整體回滾操作,也有基於保存點的部分回滾。本文將討論Django中的這兩種機制的運行原理。
Django利用django.db.transaction模塊中的API對資料庫進行事務的管理
Django provides a straightforward API in the django.db.transaction module to manage the autocommit state of each database connection.
主要函數:
1. get_autocommit(using=None)
判斷事務是否自動提交
2. set_autocommit(autocommit, using=None)
設置自動提交事務
這些函數使接受一個 using 參數表示所要操作的資料庫。如果未提供,則 Django 使用 "default" 資料庫。
3. on_commit(do something)
事務提交後馬上執行任務,例如celery任務
例如:
with transation.atomic:
#do something and commit the transaction
transaction.on_commit(lambda: some_celery_task.delay('arg1'))
怎麼使用?在哪裡使用?
事務是一系列的資料庫操作,在數據的安全性和減少網路請求方面都有很大的優勢。關於資料庫事務的文章有很多,我這裡就不展開討論了。
那麼ORM中有哪些相關的API呢?
trasation模塊中最重要的是一個Atomic類,Atomic是一個上下文管理器。可以使用@transaction.atomic 或者with transaction.atomic 的方式來調用。
為了設置保存點,即斷點進行事務的執行和回滾,可以嵌套使用with transaction.atomic,例如官網的例子(偽代碼):
with transaction.atomic(): # Outer atomic, start a new transaction transaction.on_commit(foo) #事務提交後馬上執行foo函數 try: with transaction.atomic(): # Inner atomic block, create a savepoint transaction.on_commit(bar) #事務提交後馬上執行foo函數 raise SomeError() # Raising an exception - abort the savepoint except SomeError: pass
第一個with transaction.atomic()創建事務,第二個with transaction.atomic()創建保存點。
雖然錯誤raiseSomeError是從‘內部’的保存點發出來的,但只會影響到‘外部’的保存點,即只會回滾前面的資料庫操作。
下麵還會討論另一種創建保存點的方法。
在使用transaction.atomic前需要註意的問題:
1. 資料庫的自動提交預設為開啟,如果要將它關閉,必須很小心。一旦使用了transaction,即關閉了自動提交。
2. 如果資料庫之前的使用的是自動提交,那麼在切換為非自動提交之前,必須確保當前沒有活動的事務,通常可以手動執行commit() 或者 rollback() 函數來把未提交的事務提交或者回滾。
一、整體回滾
所有的資料庫更新操作都會在一個事務中執行,如果事務中任何一個環節出現錯誤,都會回滾整個事務。
案例(偽代碼1):
from django.db import transaction # open a transaction @transaction.atomic #裝飾器格式 def func_views(request): do_something() a = A() #實例化資料庫模型 try: a.save() except DatabaseError: pass
此方案整個view都會在事務之中,所有對資料庫的操作都是原子性的。
案例(偽代碼2):
from django.db import transaction def func_views(request): try: with transaction.atomic(): #上下文格式,可以在python代碼的任何位置使用 a = A() a.save() #raise DatabaseError #測試用,檢測是否能捕捉錯誤 except DatabaseError: # 自動回滾,不需要任何操作 pass
此方案比較靈活,事務可以在代碼中的任意地方開啟,對於事務開啟前的資料庫操作是必定會執行的,事務開啟後的資料庫操作一旦出現錯誤就會回滾。
需要註意的是:
1. python代碼中對Models的修改和對資料庫的修改的區別,資料庫層面的修改不會影響Models實例變數。
如果在代碼中修改一個變數,例如:
try: with transaction.atomic(): a = A() a.attribute = True #A表的某一個屬性(即資料庫的某一列) a.save() raise DatabaseError except DatabaseError: pass print(a.attribute)
#輸出結果:True
即使資料庫回滾了,但是a實例的變數a.attribute還是會保存在Models實例中,如果需要修改,就需要在except DatabaseError後面進行。
2. transaction不需要在代碼中手動commit和rollback的。因為只有當一個transaction正常退出的時候,才會對資料庫層面進行操作。除非我們手動調用transaction.commit和transaction.rollback
實際案例(此實例用偽代碼2的格式):
models.py
數據表
class Author(models.Model): name = models.CharField(max_length=30,null=False) age = models.IntegerField() email = models.URLField(null=True) class Count(models.Model): name = models.CharField(max_length=30) article_amount = models.IntegerField()
views.py
from django.shortcuts import render from django.http import HttpResponse from index.models import Author,Count from django.db import transaction,IntegrityError def add_author_views(request): author_name = u'renyingying' author = Author(name=author_name, age=24, email='[email protected]') # author.save() count = Count(name=author_name, article_amount=1) count.save() try: with transaction.atomic(): author.save() raise DatabaseError #報出錯誤,檢測事務是否能捕捉錯誤 except DatabaseError: # 自動回滾,不需要任何操作 pass
事務外的資料庫操作正常執行,而事務內的資料庫操作則會回滾。
author表
count表
將raise DatabaseError這一行代碼註釋掉,author才會有數據
二、保存點Savepoint(斷點回滾)
保存點是事務中的標記,從原理實現上來說是一個類似存儲結構的類。可以回滾部分事務,而不是完整事務,同時會保存部分事務。python後端程式可以使用保存點。
一旦打開事務atomic(),就會構建一系列等待提交或回滾的資料庫操作。通常,如果發出回滾命令,則會回滾整個事務。保存點則提供了執行細粒度回滾的功能,而不是將執行的完全回滾transaction.rollback()。
工作原理:savepoint通過對返回sid後面的將要執行的資料庫操作進行計數,並保存在內置的列表中,當對資料庫資料庫進行操作時遇到錯誤而中斷,根據sid尋找之前的保存點並回滾數據,並將這個操作從列表中刪除。
相關API:
1. savepoint(using = None)
創建一個新的保存點。這表示處於正常狀態的事務的一個點。返回保存點ID(sid)。在一個事務中可以創建多個保存點。
2. savepoint_commit(sid,using = None)
發佈保存點sid,從創建保存點開始執行的資料庫操作將成為可能回滾事務的一部分
3. savepoint_rollback(sid,using = None)
將事務回滾到保存點sid
4. clean_savepoints(using = None)
重置用於生成唯一保存點ID的計數器
值得註意的是:
這些函數中的每一個都接受一個using參數,該參數是資料庫的名稱。如果using未提供參數,則使用"default"預設資料庫。
案例:
models.py上文的案例一樣
views.py
from django.db import transaction # open a transaction @transaction.atomic def add_author_views(request): # 自動提交方式 # Author.objects.create(name=u'wangbaoqiang',age=33,email='[email protected]') author_name = u'linghuchong' author = Author(name=author_name,age=26,email='[email protected]') author.save() # transaction now contains author.save() sid = transaction.savepoint() try: count = Count(name=author_name, article_amount=1) count.save() # transaction now contains author.save() and count.save() transaction.savepoint_commit(sid) # open transaction still contains author.save() and count.save() except IntegrityError: transaction.savepoint_rollback(sid) # open transaction now contains only count.save() # 保存author操作回滾後,事務只剩下一個操作 transaction.clean_savepoints() #清除保存點
註意:希望當遇到錯誤得到回滾的事務一定要放在try裡面(如果放在try外面,雖然不會報錯,但是是不會執行的)。如上面的例子,如果在給Count表執行插入數據發生錯誤,就會‘斷點’回滾到Count表插入數據前,Author表插入的數據不變。
結果顯示:
Author表
Count表
參考文章:
https://blog.csdn.net/m0_37422289/article/details/82221489