Bjö*_*ist 51 python django transactions race-condition
我试图在Django中以原子方式递增一个简单的计数器.我的代码看起来像这样:
from models import Counter
from django.db import transaction
@transaction.commit_on_success
def increment_counter(name):
counter = Counter.objects.get_or_create(name = name)[0]
counter.count += 1
counter.save()
Run Code Online (Sandbox Code Playgroud)
如果我正确理解Django,这应该将函数包装在事务中并使增量原子化.但它不起作用,并且在计数器更新中存在竞争条件.如何使这些代码成为线程安全的?
Odu*_*van 86
Counter.objects.get_or_create(name = name)
Counter.objects.filter(name = name).update(count = F('count')+1)
Run Code Online (Sandbox Code Playgroud)
或使用F表达式:
counter = Counter.objects.get_or_create(name = name)
counter.count = F('count') +1
counter.save( update_fields=["count"] )
Run Code Online (Sandbox Code Playgroud)
请记住指定要更新的字段,或者您可能在模型的其他可能字段上遇到竞争条件!
Xep*_*ous 12
如果您在设置时不需要知道计数器的价值,那么最佳答案肯定是您最好的选择:
counter = Counter.objects.get_or_create(name = name)
counter.count = F('count') + 1
counter.save()
Run Code Online (Sandbox Code Playgroud)
这告诉您的数据库将值加1 count
,它可以很好地完成,而不会阻止其他操作.缺点是你无法知道count
你刚刚设置了什么.如果两个线程同时命中这个函数,它们都会看到相同的值,并且都会告诉数据库添加1.数据库最终会按预期添加2,但你不会知道哪个先行.
如果您现在关心计数,可以使用select_for_update
Emil Stenstrom引用的选项.这是看起来像:
from models import Counter
from django.db import transaction
@transaction.atomic
def increment_counter(name):
counter = (Counter.objects
.select_for_update()
.get_or_create(name=name)[0]
counter.count += 1
counter.save()
Run Code Online (Sandbox Code Playgroud)
这将读取当前值并锁定匹配的行,直到事务结束.现在只有一个工人可以一次阅读.有关select_for_update的更多信息,请参阅文档.
保持简单,并建立@ Oduvan的答案:
counter, created = Counter.objects.get_or_create(name = name,
defaults={'count':1})
if not created:
counter.count = F('count') +1
counter.save()
Run Code Online (Sandbox Code Playgroud)
这里的优点是,如果对象是在第一个语句中创建的,则不必进行任何进一步的更新.
Django 1.7
from django.db.models import F
counter, created = Counter.objects.get_or_create(name = name)
counter.count = F('count') +1
counter.save()
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
27780 次 |
最近记录: |