繁体   English   中英

Django transaction.atomic()保证原子READ + WRITE?

[英]Django transaction.atomic() guarantees atomic READ + WRITE?

我需要确保从数据库读取并写回的对象不能在此期间被另一个请求/进程修改。

transaction.atomic()保证吗?

到目前为止我的测试告诉我没有。 如果他们没有任何问题,那么实现原子READS和WRITES的正确方法是什么?


我测试过的例子。

Test类放在模型中的某个位置。 atomic_test.pyatomic_test2.py应保存为管理命令。 首先运行python manage.py atomic_test ,然后运行python manage.py atomic_test2 第二个脚本不会阻止,其更改将丢失。

models.py

class Test(models.Model):
    value = models.IntegerField()

atomic_test.py

from django.core.management.base import NoArgsCommand
from django.db import transaction
from time import sleep
from core.models import Test

class Command(NoArgsCommand):
    option_list = NoArgsCommand.option_list

    def handle(self, **options):
        Test.objects.all().delete()
        t = Test(value=50)
        t.save()

        print '1 started'
        with transaction.atomic():
            t = Test.objects.all()[0]
            sleep(10)
            t.value = t.value + 10
            t.save()
        print '1 finished: %s' %Test.objects.all()[0].value

atomic_test2.py

from django.core.management.base import NoArgsCommand
from django.db import transaction
from time import sleep
from core.models import Test

class Command(NoArgsCommand):
    option_list = NoArgsCommand.option_list

    def handle(self, **options):
        print '2 started'
        with transaction.atomic():
            t = Test.objects.all()[0]
            t.value = t.value - 20
            t.save()
        print '2 finished: %s' %Test.objects.all()[0].value

Django的transaction.atomic()是对数据库事务工具的精简抽象。 所以它的行为实际上取决于数据库层,并且特定于数据库的类型及其设置。 因此,要真正了解其工作原理,您需要阅读并理解数据库的事务文档。 (例如,在PostgreSQL中,相关文档是事务隔离显式锁定 )。

至于您的特定测试用例,可以通过在Django查询集上使用select_for_update()方法来实现您想要的行为(如果您的数据库支持它)。 就像是:

在atomic_test.py中

with transaction.atomic():
    t = Test.objects.filter(id=1).select_for_update()[0]
    sleep(10) 
    t.value = t.value + 10
    t.save()

在atomic_test2.py中

with transaction.atomic():
    t = Test.objects.filter(id=1).select_for_update()[0]
    t.value = t.value - 20
    t.save()

第二个应该阻塞,直到第一个完成,并看到新值60。

其他选项包括使用SERIALIZABLE事务隔离级别或使用行锁,但Django不提供方便的API来执行这些操作。

使用F函数在数据库上进行原子更新会好得多:

from django.db.models import F
Test.objects.filter(id=1).update(value=F("value") + 10)

(这会生成类似"UPDATE test_test SET value = value + 10 WHERE id = 1"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM