以下假定所有語句都是在具有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
將等待。如何在不鎖定整個表的情況下實現此目標,這是不可接受的,因爲其他行需要保持可訪問性。
我不認爲你想要什麼是可能的,因爲你對sql語句設置的鎖沒有太大影響。當你使用innodb的時候,insert語句會一直生成獨佔意圖鎖,並且選擇update來請求獨佔鎖。 – Shadow
現在的問題是競爭條件,如果特定列已經存在,會話1將檢查'select for update',如果沒有,則創建並保存一個列。 然而,會話2並沒有看到即將出現該行,因爲處理髮生在MySQL腳本之外的地方。在該行實際存在之前可能需要幾個毫秒。我當然可以解決這個問題,但是按照我上面提到的那樣做,這將是一個更簡潔的解決方案。 – Anpan
我理解你的問題,但MySQL解決方案將爲受影響的語句使用不同類型的鎖。只要你在你選擇的語句中使用update語句,你就會被卡住。顯然,如果你在共享模式下使用鎖定,行爲會有所不同。但是,我對您的業務需求一無所知,所以我不知道這種方法是否適合您。 – Shadow