1

为了避免竞争条件,我需要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 查询启动的上一个事务。

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

4

2 回答 2

1

对于此类操作,让 Django 保留其默认的类似自动提交的行为很容易导致几种错误(根本不锁定数据库很容易成为另一种结果);详细信息可能取决于该特定 RDBMS 的 RDBMS 和/或 Django 数据库驱动程序。最好使用@commit_on_successor@commit_manuallyTransactionMiddleware

于 2013-07-18T10:31:33.790 回答
0

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

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

于 2013-07-18T10:50:40.097 回答