如何在Python/Django/MySQL中避免竞争条件?

4 投票
3 回答
5173 浏览
提问于 2025-04-16 03:46

我有一个模型叫 MyModel,它里面有一个字段叫 expiration_datetime(过期时间)。

每当用户获取 MyModel 的一个实例时,我需要先检查一下这个实例是否已经过期。如果过期了,我就需要增加一个计数器,更新其他信息,然后把过期时间延长到未来的某个时间。

所以视图的操作大概是这样的:

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最终完成时,计数器已经增加了两次,过期时间也被延长了两次。

这看起来应该是一个很常见的问题。那么处理这个问题最有效的方法是什么呢?理想情况下,我希望能够在 Django 中以数据库可移植的方式来处理。

3 个回答

0

你还可以使用 CaseWhen 这两种条件表达式。想了解更多,可以查看 文档

3

使用数据库事务。它们就是为了处理这种情况而设计的。

如果你在用MySQL,要注意只有InnoDB类型的表支持ACID事务,所以一定要确保你的表使用的是InnoDB引擎。

8

这可能是一个使用乐观锁的好例子。实现乐观锁的方法有几种:

  • 你可以给每条记录加一个版本号,然后在执行UPDATE更新时,确保在WHERE条件中包含这个版本号,这样可以检查是否有任何记录被修改过。
  • WHERE条件中包含记录的所有值(在你做出更改之前),这样你就可以确保你要保存的记录和你读取时的记录完全一样。

想知道如何在Django中实现乐观锁吗?可以看看这个问题:Django:如何防止数据库条目的并发修改

撰写回答