2014-01-25 82 views
0

我已經閱讀並測試了MySQL的InnoDB中的行級鎖,但是我仍然發現實際上很難說「我知道鎖在MySQL中的工作方式」!哪些記錄與MySQL的'SELECT ... FOR UPDATE'鎖定

這裏是我的測試數據:

mysql> select * from lockable; 
+----+----+----+ 
| id | c1 | c2 | 
+----+----+----+ 
| 1 | A | A | 
| 2 | A | B | 
| 3 | A | C | 
| 4 | B | A | 
| 5 | B | B | 
| 6 | B | C | 
| 7 | C | A | 
| 8 | C | B | 
| 9 | C | C | 
+----+----+----+ 
9 rows in set (0.00 sec) 

爲了測試MySQL的關於行級鎖的行爲,我開了兩個終端和連接到MySQL。我要命名它們mysql1>mysql2>。這是我第一個終端上執行:

mysql1> ROLLBACK; 
mysql1> BEGIN; 
mysql1> SELECT id, c1, c2 FROM lockable WHERE c1 = "A" FOR UPDATE; 
+----+----+----+ 
| id | c1 | c2 | 
+----+----+----+ 
| 1 | A | A | 
| 2 | A | B | 
| 3 | A | C | 
+----+----+----+ 
3 rows in set (0.00 sec) 

然後在第二終端上:

mysql2> SELECT * FROM lockable WHERE c1 = "B" FOR UPDATE; 

令我驚訝上面的查詢將第一個被封鎖!所以我回滾的第一終端,並開始了:

mysql1> ROLLBACK; 
mysql1> BEGIN; 
mysql1> SELECT id, c1, c2 FROM lockable LIMIT 3 FOR UPDATE; 
+----+----+----+ 
| id | c1 | c2 | 
+----+----+----+ 
| 1 | A | A | 
| 2 | A | B | 
| 3 | A | C | 
+----+----+----+ 
3 rows in set (0.00 sec) 

然後在第二終端上:

mysql2> SELECT * FROM lockable WHERE LIMIT 6,1 FOR UPDATE; 

同樣的2端通過第一個阻擋!是否有任何情況下表格的所有記錄都未被鎖定?如果不是爲什麼這被稱爲「行級鎖定」?

+0

不相關但是:在標準SQL中雙引號*僅用於標識符。要指定字符串文字,請使用單引號。 '「B」是一個列名,但「B」是一個字符串值。 MySQL接受錯誤和正確的文字,但如果您想要使用不同的DBMS,則應遵守標準兼容的方式。關於你的問題:我*認爲*(不知道)你被MySQL的「gap gap」擊中了其他行而不是你請求的行。你也可能想嘗試一個真正的'更新',而不是。也許'select .. for update'行爲有所不同。 –

+0

感謝這篇文章,我從現在開始嘗試使用'''而不是''' – Mehran

+0

Btw:你的例子在Postgres和Oracle中按預期工作 –

回答

1

回答我的問題是在MySQL's manual說:

鎖定讀,一個UPDATE或DELETE上 一般設置記錄鎖是在SQL 語句的處理掃描的每個索引記錄。

因此鎖適用於遍歷期間掃描的記錄,而不是所有記錄。如果找不到索引,則完成全掃描並鎖定所有記錄。所以我需要在列上設置一個索引,即使這樣,如果某個記錄在讀取目標記錄時被讀取,那麼它們也會被鎖定(儘管它們可能不會被最終選中)。