Select for update query: Lock wait timeout exceeded error

大憨熊 提交于 2019-12-24 12:19:43

问题


To avoid race conditions, I need to use select for update functionality while querying database so that it locks the row until the end of the transaction. Since select_for_update query is not present in Django 1.3, I have done a workaround it by using a class method which returns query sets by doing a raw sql query.

#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]

This is how it's used in views.

account = AccountDetails.get_locked_for_update(userid)
account.amount = account.amount - fare
account.save()

On the last line I'm getting this error: OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')

In dbshell, after running the save() line:

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 |
+-----+------+-----------+-----------+---------+------+----------+--------------------------    ---------------------------------------------------------------------------+

According to my understanding the lock should be released on the first data-altering query like update, delete etc.

But the save() statement is getting blocked and is kept on wait. Any idea why this is happening? What I think is when I'm calling account.save() it's not picking up the previous transaction started by select for update query.

Am I missing something obvious? Kindly help.


回答1:


Leaving Django to its default autocommit-like behaviour for such an operation can easily lead to several kinds of errors (not locking the database at all could easily be another outcome); the details probably depend on the RDBMS and/or Django database driver for that particular RDBMS. It would be better to use @commit_on_success or @commit_manually or TransactionMiddleware.




回答2:


I think some other thread is holding a record lock on some record for too long, and your thread is being timed out this a problem specific to MYSQL which does not support nowait.

You can set higher value for innodb_lock_wait_timeout and restart mysql



来源:https://stackoverflow.com/questions/17719789/select-for-update-query-lock-wait-timeout-exceeded-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!