为了避免竞争条件,我需要select for update
在查询数据库时使用功能,以便它锁定行直到事务结束。由于 Django 1.3 中不存在 select_for_update 查询,因此我通过使用类方法来解决它,该类方法通过执行原始 sql 查询返回查询集。
#models.py
class AccountDetails(models.Model):
user = models.OneToOneField(User)
amount = models.IntegerField(max_length=15,null=True,blank=True)
@classmethod
def get_locked_for_update(cls,userid):
accounts = cls.objects.raw("SELECT * FROM b2b_accountdetails WHERE user_id ="+str(userid)+" FOR UPDATE")
return accounts[0]
这就是它在视图中的使用方式。
account = AccountDetails.get_locked_for_update(userid)
account.amount = account.amount - fare
account.save()
在最后一行我收到此错误:
OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')
在 dbshell 中,运行该save()
行后:
mysql> SHOW FULL PROCESSLIST;
+-----+------+-----------+-----------+---------+------+----------+-----------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+-----------+---------+------+----------+-------------------------- ---------------------------------------------------------------------------+
| 51 | root | localhost | dbname | Query | 0 | NULL | SHOW FULL PROCESSLIST |
| 767 | root | localhost | dbname | Sleep | 59 | | NULL |
| 768 | root | localhost | dbname | Query | 49 | Updating | UPDATE `b2b_accountdetails` SET `user_id` = 1, `amount` = 68906 WHERE `appname_accountdetails`.`id` = 1 |
+-----+------+-----------+-----------+---------+------+----------+-------------------------- ---------------------------------------------------------------------------+
根据我的理解,应该在第一个数据更改查询(如更新、删除等)上释放锁。
但该save()
声明被阻止并一直在等待。知道为什么会这样吗?我的想法是,当我调用account.save()
它时,它不会获取由 select for update 查询启动的上一个事务。
我错过了一些明显的东西吗?请帮忙。