简体   繁体   English

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

[英]How do you avoid this race condition in Python / Django / MySQL?

I have a model MyModel that has a field expiration_datetime. 我有一个模型MyModel,它有一个字段expiration_datetime。

Every time a user retrieves an instance of MyModel I need to first check if it has expired or not. 每次用户检索MyModel的实例时,我都需要先检查它是否已过期。 If it has expired, than I need to increment some counter, update others, and then extend the expiration_datetime to some time in the future. 如果它已经过期,我需要增加一些计数器,更新其他计数器,然后将expiration_datetime扩展到将来的某个时间。

So the view would do something like: 所以视图会做类似的事情:

if object.expiration_datetime < datetime.datetime.now(): 
    object.counter = F('counter') + 1 
    object.expiration_datetime = F('expiration_datetime') + datetime.timedelta(days=1) 
    object.save() 

There's a race condition in the code above. 上面的代码中存在竞争条件。 Say thread 1 checks and finds that the current instance has expired, it proceeds to increment the counter and extend the expiration datetime. 假设线程1检查并发现当前实例已过期,它继续增加计数器并延长到期日期时间。 But before it could do so, thread 2 is scheduled and does the same. 但是在它可以这样做之前,线程2被安排并且做同样的事情。 By the time thread 1 finally finishes, counter has been incremented twice and expration_datetime has been extended twice. 到线程1最终完成时,计数器已递增两次,expration_datetime已延长两次。

This looks like it should be a pretty common issue. 这看起来应该是一个非常常见的问题。 What is the most efficient way to handle it? 处理它的最有效方法是什么? Ideally I'd like to be able to handle it within Django in a database-portable way. 理想情况下,我希望能够以数据库可移植的方式在Django中处理它。

This may be a good use case for optimistic locking . 这可能是乐观锁定的一个很好的用例。 There's a few ways to do it: 有几种方法可以做到:

  • You can have a version number, and you run your UPDATE queries so that it always includes the version number in the WHERE clause, and then check if any rows were changed. 您可以拥有版本号,并运行UPDATE查询,以便始终在WHERE子句中包含版本号,然后检查是否有任何行已更改。
  • Include every value of the record (before the changes you made) in the WHERE clause, that way you can be sure that the record you are saving is exactly the same as when you read it. WHERE子句中包含记录的每个值(在您所做的更改之前),这样您就可以确保保存的记录与读取时的记录完全相同。

How to implement optimistic locking in Django? 如何在Django中实现乐观锁定? Check out this question: Django: How can I protect against concurrent modification of data base entries . 看看这个问题: Django:我如何防止数据库条目的并发修改

Use a database transaction. 使用数据库事务。 They're designed to handle cases exactly like this. 它们旨在处理完全像这样的情况。

If you're using MySQL, be aware that only InnoDB tables support ACID transactions, so make sure your tables use the InnoDB engine. 如果您使用的是MySQL,请注意只有InnoDB表支持ACID事务,因此请确保您的表使用InnoDB引擎。

You can also use Case and When conditional expressions. 您还可以使用CaseWhen条件表达式。 Docs . 文件

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

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