2013-07-18 97 views
1

爲避免競爭條件,我需要在查詢數據庫時使用select for update功能,以便鎖定行直到事務結束。由於select_for_update查詢在Django 1.3中不存在,我通過使用通過執行原始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() st困境越來越嚴重,並且一直在等待。任何想法爲什麼發生這種情況?當我打電話給account.save()時,我認爲它沒有選擇前一個事務,而是選擇更新查詢。

我錯過了一些明顯的東西嗎?請幫助。

+0

是否使用TransactionMiddleware?這個問題是否發生在HTTP請求的上下文中? –

+0

@AntonisChristofides否我沒有使用TransactionMiddleware。是的,它發生在HTTP請求的上下文中,但不僅僅發生在HTTP請求的上下文中。當我從django shell運行代碼時出現同樣的錯誤。 – Sudipta

回答

1

使Django離開其默認的類似於自動提交的行爲可能很容易導致出現幾種錯誤(根本不鎖定數據庫可能很容易成爲另一種結果);細節可能取決於該特定RDBMS的RDBMS和/或Django數據庫驅動程序。最好使用@commit_on_success or @commit_manually or TransactionMiddleware

+0

我嘗試了這兩種方法,但它不起作用。請看我更新的問題。當我做'account.save()'創建一個新的線程。因此它被阻塞,因爲前一個線程已經鎖定了該行。 – Sudipta

+0

只是爲了確定:你試過'@ commit_manually'來裝飾包含'account.save()'的函數嗎? –

+0

是的,我將選擇查詢中的部分移動到另一個函數中,並僅將該修飾器應用於該函數,而不是視圖。 – Sudipta

0

我認爲其他線程在某些記錄上持有記錄鎖的時間太長,並且您的線程正在超時,這是MYSQL特有的問題,它不支持nowait。

您可以innodb_lock_wait_timeout設定更高的價值和重啓MySQL

+0

不,我想我可以放心地排除任何其他線程持有記錄的可能性。因爲即使我殺死了SHOW SHOW FULL PROCESSLIST中顯示的所有線程,也會出現此問題。但是,此調試確認了我的初步猜測:我的選擇查詢和更新查詢在兩個不同的線程上運行。我會在我的問題中更新這個發現。 – Sudipta

相關問題