在没有竞争条件的情况下在 Django 中进行条件更新
Conditional updating in Django without race condition
如何根据当前值更新模型字段并避免竞争条件?更新任务可以写成:
if (self.x == y):
self.x = z
self.save()
else:
raise Exception()
但是,存在竞争条件。我提出了以下解决方案:
from django.db import transaction
with transaction.atomic():
if (self.x == y):
self.x = z
self.save()
else:
raise Exception()
但是这样安全吗?还有更好的方法吗?
不,那个 atomic()
块不会做任何事情,因为在您尝试 运行 交易之前,您已经从数据库中提取了值并输入 self
。
如果您可以在查询参数中表达您的条件,您可以使用 update()
:
在单个查询中安全地执行此操作
num_matched = MyModel.objects.filter(id=self.id, x=y).update(x=z)
if num_matched != 1:
raise Exception()
如果没有,可以使用select_for_update()
:
with transaction.atomic():
current = MyModel.objects.select_for_update().get(id=self.id)
if (current.x == y):
current.x = z
current.save()
else:
raise Exception()
此代码与上面的代码之间的区别在于,此处您在进行比较之前明确告诉数据库要锁定的行。
如何根据当前值更新模型字段并避免竞争条件?更新任务可以写成:
if (self.x == y):
self.x = z
self.save()
else:
raise Exception()
但是,存在竞争条件。我提出了以下解决方案:
from django.db import transaction
with transaction.atomic():
if (self.x == y):
self.x = z
self.save()
else:
raise Exception()
但是这样安全吗?还有更好的方法吗?
不,那个 atomic()
块不会做任何事情,因为在您尝试 运行 交易之前,您已经从数据库中提取了值并输入 self
。
如果您可以在查询参数中表达您的条件,您可以使用 update()
:
num_matched = MyModel.objects.filter(id=self.id, x=y).update(x=z)
if num_matched != 1:
raise Exception()
如果没有,可以使用select_for_update()
:
with transaction.atomic():
current = MyModel.objects.select_for_update().get(id=self.id)
if (current.x == y):
current.x = z
current.save()
else:
raise Exception()
此代码与上面的代码之间的区别在于,此处您在进行比较之前明确告诉数据库要锁定的行。