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

kev*_*kev 9 python django concurrency transactions atomic

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

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()
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Kev*_*nry 13

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

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

在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()
Run Code Online (Sandbox Code Playgroud)

在atomic_test2.py中

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

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

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