今天在调试项目开发好的一个模块的时候,发现了一个很诡异的现象,最后追踪发现是因为在项目中事务处理有误所致。这个问题坑了我好一会,所以记录一下,以免再踩坑。下面开始详述。

我们都知道 Django 框架提供了很多的开启事务的方式,这在后面会有详述。笔者比较喜欢使用的是使用 @transaction.atomic 装饰的方式来启动一个事务。因为通过该形式,我们可以在保证了 db 原子操作的同时,还可以自定义事务涉及的模块范围。atomic 还可以通过上下文的形式来使用,比如:

with transaction.atomic():
transaction_plalala()
.
.
.

好了。既然如此,那就用起来吧。一阵啪啪啪之后,开发完了,调试的 log 也打了,开测吧。诡异的事情在此发生了。在log中明明打印出来了数据库中生成的主键id,但是在数据库中死活查不出来。WTF?!通过 SHOW CREATE TABLE xxx 也看到了 xxx 表的自增值已经发现了变化。但咋就没有了呢?谁动了我的数据?这时候,不得不从 view 开始看起,一直追踪到了开发的新的模块。view到调用模块没有问题。这是什么情况?有鬼?肯定不是。莫非在某个地方,配置了一个新的事务,而这个事务是包含了整个 view?因为笔者只发现了 view 中有个 raise exception的操作。猜测只能是这样了。因为我新开发的模块没有问题的,这我是在其他的 view 中进行过验证的。

于是乎,笔者看了下 Django 中的事务开启的方式,发现了果然有一个将事务绑定到 HTTP 请求上的开启的方式。它的开启方式是在 db 中指定 ATOMIC_REQUESTS=True 开启。打开 settings 文件,找到了对应的配置,果然,问题就是出在这里!咋办?既然这边人家已经配置了,在不影响到其他 view(说不定已经依赖了此事务操作)的情况下,怎么去关闭这个配置呢?答案是通过 transaction.non_atomic_requests 装饰view。好了。测试一下,确实可以了。问题解决了。下面的是官方 Demo:

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 中的官方文档后,发现,其不推荐这么做。原因是如果将事务跟 HTTP 请求绑定到一起的话,view 是依赖于应用程序对数据库的查询语句效率和数据库当前的锁竞争情况。当流量上来的时候,性能会有影响。那么,该怎么去保证既可以使用事务呢?

第一种就是上面所说的这种,在 database 中通过指定 ATOMIC_REQUESTS 的形式来将事务绑定到HTTP请求上。接触 view 在事务中的操作的方式是在 view 上装饰 transaction.non_atomic_requests,前面已经说过,具体也可以阅读 Django 官方文档。

default_db = {
"ENGINE": "",
"NAME": "",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
"OPTIONS": "",
"ATOMIC_REQUESTS": True,
}

还有一种方式是笔者喜欢用的那种,通过 transaction.atomic 来更加明确的控制事务。atomic允许我们在执行代码块时,在数据库层面提供原子性保证。 如果代码块成功完成, 相应的变化会被提交到数据库进行commit;如果执行期间遇到异常,则会将该段代码所涉及的所有更改回滚。

这里,我们在使用此方式的时候还需要注意一点,就是:避免在 atomic里捕获异常!当一个原子块执行完退出时,Django会审查是正常提交还是回滚。如果你在原子块中捕获了异常的句柄, 你可能就向 Django 隐藏了问题的发生。这可能会导致意想不到的后果。正确捕捉数据库异常应该是类似上文所讲 ,基于atomic 代码块来做。若有必要,可以额外增加一层atomic代码来用于此目的。这种模式还有另一个优势:它明确了当一个异常发生时,哪些操作将回滚。在底层,Django的事务管理代码:

  • 当进入到最外层的 atomic 代码块时会打开一个事务;
  • 当进入到内层atomic代码块时会创建一个保存点;
  • 当退出内部块时会释放或回滚保存点;
  • 当退出外部块时提交或回退事物。

    你可以通过设置savepoint 参数为 False来使对内层的保存点失效。如果异常发生,若设置了savepoint,Django会在退出第一层代码块时执行回滚,否则会在最外层的代码块上执行回滚。 原子性始终会在外层事物上得到保证。这个选项仅仅用在设置保存点开销很明显时的情况下。它的缺点是打破了上述错误处理的原则。
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()

Django 支持 autocommit 来为每个SQL语句在执行时都会启动一个事务。如果想要关闭,可以通过在配置文件中设置 AUTOCOMMIT=False 参数来关闭。这样,Django 将不能启用 autocommit,也不能执行任何 commits。这就需要你对每个事物执行明确的commit操作。因此,这最好只用于你自定义的事物控制中间件或者是一些比较奇特的场景。

参考:

Django transaction 误用之后遇到的一个问题与解决方法的更多相关文章

  1. 一个简单的解决方法:word文档打不开,错误提示mso.dll模块错误。

    最近电脑Word无故出现故障,无法打开,提示错误信息如下: 问题事件名称: APPCRASH应用程序名: WINWORD.EXE应用程序版本: 11.0.8328.0应用程序时间戳: 4c717ed1 ...

  2. C# 该行已经属于另一个表 的解决方法[转]

    该文转自:http://blog.sina.com.cn/s/blog_48e4c3fe0100nzs6.html DataTable dt = new DataTable(); dt = ds.Ta ...

  3. Emgu CV的一个异常的解决方法

    今年组里有大项目落我头上了,并不能像去年一样回家还能搞搞Cocos2dX,一把老泪流了下来... 回到正题,由于组里需要做一个显示板的自动测试项目,涉及到Computer Vision.不得不说,这才 ...

  4. python 迭代器 一个奇怪的解决方法

    一般我们在类里面写迭代器都是如下写法: class IterableSomthing: def __iter__(self): return self def __next__(self): retu ...

  5. django框架使用mysql报错,及两种解决方法

    1.django框架 settings.py文件中部分代码: DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3' ...

  6. The “SignFile” task was not given a value for the required parameter “CertificateThumbprint”的一个简单的解决方法

    这个只是其中一种解决方法,而且不是万能的 1. 由提示内容可以看出,这个一个 sign(认证)的问题, 在出现这个问题的项目上,鼠标右键,选择properties,然后选择signing. 2. 选择 ...

  7. confirm显示数组中的内容时,总是带一个逗号分隔的解决方法

    问题的关键 就是在给confirm显示之前,将数组转换成字符串,并以每个数组的元素为一个字符串,加上一个换行回车符即可: 代码中的背景色 为关键的点 <script type="tex ...

  8. export的变量另开一个终端失效解决方法

    有时候,我们需要把一个export的变量全局话,否则每开一个终端又需要重新export,很是麻烦 首先直接export某个变量的话就只能在当前子终端生效,另开一个终端就失效了 如果修改.bash_pr ...

  9. C#两个DataTable拷贝问题:该行已经属于另一个表的解决方法

    一.DataTable.Rows.Add(DataRow.ItemArray); 二.DataTable.ImportRow(DataRow) 三.设置DataTable的tablename,然后.R ...

随机推荐

  1. arcmap 设置线段的不同颜色(及其它转化)

    一: shp 转化为 mxd或导出地图  当时做的第一个shp文件,应该是研一的第二个学期了,都不记得是怎么操作的了. 通过file另存为mxd就可以生成各个shp的arcmap能够直接打开的mxd文 ...

  2. TOMCATserver不写port号、不写项目名訪问项目、虚拟文件夹配置

    一.不写port. 这个问题都被问烂了.由于TOMCAT默认的訪问port为8080.而TCP/IP协议默认80port訪问,大家之所以看到别的站点都不写port号是由于人家用的的80port訪问的, ...

  3. QTP11.5公布,改名UFT

    QTP11.5公布,改名UFT,支持多脚本编辑调试.PDF检查点.持续集成系统.手机測试等 http://www.learnqtp.com/hp-uft11-5-qtp-new-features/ T ...

  4. tomcatserver管理界面username和password忘记

    tomcatserverhttp://localhost:8080/ 这样訪问,点击Manager App后要求输入username和password才干进入管理应用界面 我忘记了username和p ...

  5. DDos游戏行业受攻击最多

    游戏行业遭遇DDOS攻击现状. 游戏一直是最易遭受黑客攻击的行业,高居全年DDOS攻击的48%.大规模攻击居多,平均值均超过100Gbps,攻击峰值急速上升,同比2015年增加了137.1%,其中攻击 ...

  6. Tuples are immutable

    A tuple is a sequence of values. The values can be any type, and they are indexed by integers, so in ...

  7. js字符串排序方法

    前端开发过程中有时需自己手写排序方法 一般想到数字的字符串排序方法 我们会用到 var newArr = arr. sort(function(a,b){return a - b})来进行排序 但除此 ...

  8. Vector源码学习

    安全的可增长数组结构 实现: 1. 内部采用数组的方式. 1.1 添加元素,会每次校验容量是否满足, 扩容规则有两种,1.增加扩容补偿的长度,2.按照现有数组长度翻一倍.容量上限是Integer.MA ...

  9. 20180929 北京大学 人工智能实践:Tensorflow笔记05

    (完)

  10. 洛谷—— P1926 小书童——刷题大军

    https://www.luogu.org/problem/show?pid=1926#sub 题目背景 数学是火,点亮物理的灯:物理是灯,照亮化学的路:化学是路,通向生物的坑:生物是坑,埋葬学理的人 ...