Django Transactions,select_for_update,并发修改?

Django Transactions, select_for_update, concurrent modification?

我有一堆视图,我在执行之前检查某些条件是否为真。先执行哪个视图并不重要,但我担心一个用户使用一个视图,该视图在另一个视图中检查了条件后更改了数据。非常感谢对此的任何帮助。

我正在使用设置 ATOMIC_REQUESTS = True 的 2 勺建议,将每个请求包装在一个事务中。我的网站流量相对较低,所以我还不关心性能。也使用 postgres。

def feed_dog(self, dog_id):
    dog = get_objects_or_404(Dog, pk=dog_id)
    if not dog.removed and not dog.fed_today:    # line 3
        # do additional checks
        dog.fed_today = True    # line 5
        dog.save()
        # also modify other related objects
        treat = Treats.objects.last()
        treat.dogs_being_fed_with_this_treat.add(dog)
        treat.save()

def remove_dog_from_feed_list(self, dog_id):
    dog = get_objects_or_404(Dog, pk=dog_id)
    if not dog.fed_today           # line 12
    dog.removed = True
    dog.save()
    treat.dogs_being_fed_with_this_treat.remove(dog)
    treat.save()

所以我担心的是有人调用了feed_dog 视图,然后在检查第3 行之后但在第5 行开始之前,另一个用户调用了remove_dog 函数。由于当用户 2 调用 remove_dog 时 dog.fed_today 仍然为 False,第 12 行为 True,并且 remove_dog 视图继续并执行 dog.removed = True。同时 feed_dog 视图继续并设置 dog.fed_today=True,将狗添加到 treat 数组等。所以我有 dog.removed = True 的不一致状态,并且 dog.fed_today = 正确。

基本上我需要它,这样 Dog 模型上的某些属性不能同时为 True,例如 dog.fed_today =True 和 dog.removed=True。我也有这个相关的模型 Treat,我不希望狗对象已删除 = True 并且也位于 treat.dogs_being_fed 数组中。

问:这是一个合理的担忧吗?由于事务提供隔离,这是否意味着其中一个视图在另一个视图之前或之后访问数据(它们看不到半完成状态的数据)?

问:增加 Django/Postgres 与 Read Committed 的隔离度 属性 是否有帮助?

问:对 Dog 模型本身进行约束是否会有所帮助(比如使用 def clean 方法来说明 Dog 不能同时被移除 = True 和 fed_today = True?)。我主要是在查看视图中的内容。这会反映在交易中吗?我是否也可以创建一个约束来反映多个模型中的属性,比如不能有 dog.removed=True 并且让那只狗在 Treats.dogs_being_fed 数组中?

问:select_for_update 的目的是什么,对这里有帮助吗?如果事务已经应该提供隔离,那么 select_for_update 的目的是什么?

感谢任何帮助!

除非您了解数据库隔离级别,否则您真的无法有效地使用事务。如果您使用的是 PostgreSQL,请查看 their documentation on the subject.

回答您的具体问题:

这是一个有效的问题吗?

当然可以。默认的 READ COMMITTED 隔离级别不会保护数据库不受上面代码的影响。

由于事务提供了隔离,这是否意味着其中一个视图在另一个视图之前或之后访问数据(他们看不到半成品中的数据)状态)?

没有。不过,这大约就是 SERIALIZABLE 隔离级别的含义。

是否有助于增加 Django / Postgres 与 READ COMMITTED 的隔离 属性?

是的,但您通常不需要更严格的隔离级别,并且由于使用它们会降低性能,因此重新考虑您的数据库访问模式通常更有意义。

Dog 模型本身进行约束(例如使用 clean() 方法)是否有帮助?

没有。 Django 的验证方法应用于查询数据库后创建的 Python 对象。它们无助于防止竞争条件引起的数据损坏。 (不过,他们可能会在事后帮助检测它。)

如果事务本来就应该提供隔离,那么select_for_update()的目的是什么?

select_for_update() 执行 SELECT 但会锁定匹配的行,以便它们不能在其他事务中同时修改。如前所述,这不是事务隔离级别的作用。

对这里有帮助吗?

是的!这是您问题的最简单解决方案,因为您选择(并且可以锁定)Dog table 中的一行。如果两个函数都使用

dog = Dog.objects.filter(pk=dog_id).select_for_update().get()

然后他们都会尝试锁定有问题的行。如果有并发修改的尝试,第二个将等到第一个事务结束后再继续。