【发布时间】:2013-07-18 09:37:33
【问题描述】:
为了避免竞争条件,我需要在查询数据库时使用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 查询启动的上一个事务。
我是否遗漏了一些明显的东西?请帮忙。
【问题讨论】:
-
你在使用 TransactionMiddleware 吗?这个问题是否发生在 HTTP 请求的上下文中?
-
@AntonisChristofides 不,我没有使用 TransactionMiddleware。是的,它发生在 HTTP 请求的上下文中,但不仅在 HTTP 请求的上下文中。当我从 django shell 运行代码时,也会出现同样的错误。
标签: python mysql django select locking