2017-06-19 92 views
2

以下假定所有語句都是在具有InnoDB表的MySQL 5.6上的事務中執行的。另外,col1和col2一起構成一個唯一的鍵。FOR UPDATE的MySQL讀鎖定

如果我在會話1中做了SELECT * FROM table WHERE col1 = 1 AND col2 = 1 FOR UPDATE,我從該行獲取數據並在該行獲得排它鎖。因此,如果我在會話2上執行相同的語句,它將等待,直到執行任何操作或超時之前釋放該鎖。到現在爲止還挺好。

現在假設SELECT ... FOR UPDATE返回一個空集,我們嘗試在另一個會話中插入了一句:

-- Session 1 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE; 
Empty set (0.01 sec) 
-- Do whatever else takes some time 

-- Session 2 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO `table` SET `col1` = 1, `col2` = 1; 
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 

此行爲是因爲我希望它。但是,如果我們這樣做:

-- Session 1 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE; 
Empty set (0.00 sec) 

-- Session 2 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE; 
Empty set (0.00 sec) 

mysql> INSERT INTO `table` SET `col1` = 1, `col2` = 1; 
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 

現在,其實我是想的是,在相同的(尚未存在的)排第二SELECT ... FOR UPDATE會以同樣的方式的INSERT INTO將等待。如何在不鎖定整個表的情況下實現此目標,這是不可接受的,因爲其他行需要保持可訪問性。

+0

我不認爲你想要什麼是可能的,因爲你對sql語句設置的鎖沒有太大影響。當你使用innodb的時候,insert語句會一直生成獨佔意圖鎖,並且選擇update來請求獨佔鎖。 – Shadow

+0

現在的問題是競爭條件,如果特定列已經存在,會話1將檢查'select for update',如果沒有,則創建並保存一個列。 然而,會話2並沒有看到即將出現該行,因爲處理髮生在MySQL腳本之外的地方。在該行實際存在之前可能需要幾個毫秒。我當然可以解決這個問題,但是按照我上面提到的那樣做,這將是一個更簡潔的解決方案。 – Anpan

+0

我理解你的問題,但MySQL解決方案將爲受影響的語句使用不同類型的鎖。只要你在你選擇的語句中使用update語句,你就會被卡住。顯然,如果你在共享模式下使用鎖定,行爲會有所不同。但是,我對您的業務需求一無所知,所以我不知道這種方法是否適合您。 – Shadow

回答

0

當您讓事務運行超過幾秒鐘時,您就會發現問題。編寫代碼以便更快完成。你的第一個代碼樣本不是innodb_lock_wait_timeout,默認爲50秒。

您必須編寫代碼來捕捉錯誤 - 例如您得到的兩個不同的錯誤 - 並採取回避措施。該行動通常是開始整個交易。屆時,另一場會議很可能已經完成,無論它正在做什麼,你應該通過'即時'。

如果你需要一個非常長的鎖 - 比如一個用戶在網頁中跳躍幾分鐘的購物車,你必須使用其他機制來「鎖定」,而不是InnoDB的事務鎖定。

如果您想進一步描述您的應用程序,我們可以進一步討論。

+0

這實際上只是我爲了說明目的手動進行這個過程。在實際使用情況中,我們正在討論兩個並行進程,這些進程可能在不到一毫秒的時間內以我所顯示的方式訪問數據。 – Anpan

+0

毫秒級的「COMMIT」? 「別的什麼都需要一些時間」把我扔掉。 –

+0

事實上,直到提交的整個過程在毫秒內完成。問題是兩個進程幾乎同時啓動,但我需要其中一個進程來等待這一行,直到另一個進程完成。 – Anpan