我有一个模型MyModel,它有一个字段expiration_datetime。
每次用户检索MyModel的实例时,我都需要先做 检查它是否已过期。如果它已经过期,那么我需要 增加一些计数器,更新其他计数器,然后扩展 expiration_datetime到将来的某个时间。
所以视图会做类似的事情:
if object.expiration_datetime < datetime.datetime.now():
object.counter = F('counter') + 1
object.expiration_datetime = F('expiration_datetime') + datetime.timedelta(days=1)
object.save()
上面的代码中存在竞争条件。说线程1检查和 发现当前实例已过期,它继续增加 计数器并延长到期日期。但在它可以做之前 因此,线程2被安排并执行相同的操作。到时候线程1 终于完成了,计数器已经增加了两次 expration_datetime已经延长了两次。
这看起来应该是一个非常常见的问题。处理它的最有效方法是什么?理想情况下,我希望能够以数据库可移植的方式在Django中处理它。
答案 0 :(得分:6)
这可能是optimistic locking的一个很好的用例。有几种方法可以做到:
UPDATE
个查询,使其始终包含WHERE
子句中的版本号,然后检查是否有任何行已更改。WHERE
子句中包含记录的每个值(在您所做的更改之前),这样就可以确保您保存的记录与您阅读时的记录完全相同。如何在Django中实现乐观锁定?看看这个问题:Django: How can I protect against concurrent modification of data base entries。
答案 1 :(得分:3)
使用数据库事务。它们旨在处理与此类似的情况。
如果您使用的是MySQL,请注意只有InnoDB表支持ACID事务,因此请确保您的表使用InnoDB引擎。
答案 2 :(得分:0)
您还可以使用Case
和When
条件表达式。 Docs