在django怎么处理这种比赛状况? [英] How do I deal with this race condition in django?
问题描述
这段代码应该是获取或者创建一个对象,如果需要的话更新它。代码在网站上生产使用。
This code is supposed to get or create an object and update it if necessary. The code is in production use on a website.
在某些情况下 - 数据库正忙时,会抛出异常DoesNotExist:MyObj匹配查询不存在。
In some cases - when the database is busy - it will throw the exception "DoesNotExist: MyObj matching query does not exist".
# Model:
class MyObj(models.Model):
thing = models.ForeignKey(Thing)
owner = models.ForeignKey(User)
state = models.BooleanField()
class Meta:
unique_together = (('thing', 'owner'),)
# Update or create myobj
@transaction.commit_on_success
def create_or_update_myobj(owner, thing, state)
try:
myobj, created = MyObj.objects.get_or_create(owner=user,thing=thing)
except IntegrityError:
myobj = MyObj.objects.get(owner=user,thing=thing)
# Will sometimes throw "DoesNotExist: MyObj matching query does not exist"
myobj.state = state
myobj.save()
我在ubuntu上使用innodb mysql数据库。
I use an innodb mysql database on ubuntu.
我如何安全地处理wi这个问题?
How do I safely deal with this problem?
推荐答案
这可能是一个与此相同的问题的脱机:
This could be an off-shoot of the same problem as here:
基本上 get_or_create 可以失败 - 如果你看看它的来源,会看到它是:get,if-problem:save + some_trickery,if-still-problem:再一次,if-still-problem:投降和提高。
Basically get_or_create can fail - if you take a look at its source, there you'll see that it's: get, if-problem: save+some_trickery, if-still-problem: get again, if-still-problem: surrender and raise.
这意味着如果有两个并发线程(或进程)运行 create_or_update_myobj
,那么尝试get_or_create相同对象,然后:
This means that if there are two simultaneous threads (or processes) running create_or_update_myobj
, both trying to get_or_create the same object, then:
- 第一个线程尝试获取它 - 但它还不存在,
- 所以,线程尝试创建它,但是在创建对象之前...
- ...第二个线程尝试获取它 - 这显然失败了
- 现在,由于MySQLdb数据库连接的默认AUTOCOMMIT = OFF,REPEATABLE READ可序列化级别,两个线程都冻结了MyObj表的视图。
- 线程创建它的对象并优雅地返回它,但是...
- ...第二个线程不能创建任何东西,因为它会违反
unique
约束 - 有趣的是,第二个线程的后续
get
不会看到第一个线程中创建的对象,由于冻结查看MyObj表
- first thread tries to get it - but it doesn't yet exist,
- so, the thread tries to create it, but before the object is created...
- ...second thread tries to get it - and this obviously fails
- now, because of the default AUTOCOMMIT=OFF for MySQLdb database connection, and REPEATABLE READ serializable level, both threads have frozen their views of MyObj table.
- subsequently, first thread creates its object and returns it gracefully, but...
- ...second thread cannot create anything as it would violate
unique
constraint - what's funny, subsequent
get
on the second thread doesn't see the object created in the first thread, due to the frozen view of MyObj table
所以,如果你想安全地 get_or_create
任何东西,尝试这样:
So, if you want to safely get_or_create
anything, try something like this:
@transaction.commit_on_success
def my_get_or_create(...):
try:
obj = MyObj.objects.create(...)
except IntegrityError:
transaction.commit()
obj = MyObj.objects.get(...)
return obj
于27/05/2010编辑
还有第二个解决方案 - 使用READ COMMITED隔离级别,而不是REPEATABLE READ。但是它的测试较少(至少在MySQL中),所以可能会有更多的错误/问题 - 但是至少它可以将交易的交易视图绑定在中间。
Edited on 27/05/2010
There is also a second solution to the problem - using READ COMMITED isolation level, instead of REPEATABLE READ. But it's less tested (at least in MySQL), so there might be more bugs/problems with it - but at least it allows tying views to transactions, without committing in the middle.
这是关于MySQL和Django的一些很好的博客文章(不是我的),与这个问题有关:
Here are some good blog posts (not mine) about MySQL and Django, related to this question:
http: //www.no-ack.org/2010/07/mysql-transactions-and-django.html
http://www.no-ack.org/2011/05/broken- transaction-management-in-mysql.html
这篇关于在django怎么处理这种比赛状况?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!