选择更新查询:超出锁定等待超时错误

时间:2013-07-18 09:37:34

标签: python mysql django select locking

为避免竞争条件,我需要在查询数据库时使用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')

在dbshel​​l中,运行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 query启动的先前交易。

我错过了一些明显的东西吗?请帮助。

2 个答案:

答案 0 :(得分:1)

将Django留给其默认的类似自动提交行为这样的操作很容易导致几种错误(根本不能锁定数据库很容易成为另一种结果);细节可能取决于特定RDBMS的RDBMS和/或Django数据库驱动程序。最好使用@commit_on_success or @commit_manually or TransactionMiddleware

答案 1 :(得分:0)

我认为其他一些线程在某些记录上持有记录锁定的时间太长,并且你的线程正在超时这是一个特定于MYSQL的问题,它不支持nowait。

您可以为innodb_lock_wait_timeout设置更高的值并重新启动mysql