?本文在鎖概述的基礎上,通常實驗舉例,詳細地介紹了意向鎖的原理。
鎖范圍?
全局鎖(global lock)
表鎖(table lock)
行鎖 (row lock)
ROW LOCK的粒度
LOCK_REC_NOG_GAP, record lock with out gap lock
LOCK_GAP, gap lock
LOCK_ORDINARY , next key lock = record lock + gap lock
鎖等待與死鎖
鎖等待 事務提交或等待超時;死鎖,是一個死循環。死鎖中必有鎖等待。
表鎖
5.5以后在server層實現表鎖
innodb中有IS/IX表級鎖,以及自增鎖(auto-inc)
讀鎖
加讀鎖后,只能對表讀,不能對表寫;允許多個會話同時讀;其他會話可以加共享讀鎖
lock table?table_name read
寫鎖
lock table table_name write
持有鎖的會話可寫可讀
其他會話訪問表或請求加鎖都會被阻塞,直到鎖釋放
釋放鎖
unlock tables;
lock table 鎖不能相互嵌套,一個事務開始就意味著另外一個事務結束
顯式開啟一個事務,因為事務中不能支持表鎖,所以事務開始則表鎖斷開
Kill或連接斷開
innodb鎖
默認為行鎖
在索引上加鎖來實現行鎖
如果沒有索引,那么升級為全表記錄鎖,最終效果等同于表鎖;但表鎖只需要在根節點上加鎖,而不是對所有記錄加鎖,所以代價要小一些
?
鎖類型
共享鎖
排他鎖/獨占鎖
意向鎖,innodb特有,加在表級別上的鎖
共享鎖與獨占鎖均用于事務當中,隨事務的結束而解除。
共享鎖(share lock)
又稱讀鎖,讀取操作創建的鎖。
一旦上鎖,任何事務(包括當前事務)無法對其修改,其他事務可以并發讀取數據,也可在對此數據再加共享鎖
語法:SELECT ... LOCK IN SHARE MODE;
排他鎖(exclusive lock)
又稱寫鎖,如果事務對數據A加上排他鎖后,則其他事務不可并發讀取數據,也不能再對A加任何類型的鎖。獲得排他鎖的事務既能讀數據,又能修改數據。
語法:SELECT ... FOR UPDATE
這里的“其他事務不可并發讀取數據”,指的是不可以加共享鎖,即不可以以lock in share mode的方式讀取數據,比如
會話一
mysql> select * from test for update; +----+------+------+ | id | v1 | v2 | +----+------+------+ | 1 | 1 | 0 | | 2 | 3 | 1 | | 3 | 4 | 2 | | 5 | 5 | 3 | | 7 | 7 | 4 | | 10 | 9 | 5 | +----+------+------+ 6 rows in set (0.00 sec)
會話二:
mysql> select * from test lock in share mode; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
直接查詢完全可以
mysql> select * from test; +----+------+------+ | id | v1 | v2 | +----+------+------+ | 1 | 1 | 0 | | 2 | 3 | 1 | | 3 | 4 | 2 | | 5 | 5 | 3 | | 7 | 7 | 4 | | 10 | 9 | 5 | +----+------+------+ 6 rows in set (0.00 sec)
這是因為直接查詢走的是一致性快照讀,讀的是MVCC版本控制下的快照,不加鎖;換句話說,排他鎖排斥的“讀”,是指S鎖下的讀,或者說是當前讀
意向鎖
InnoDB的表級鎖,其設計目的主要是為了在一個事務中揭示下一步將要被請求的鎖的類型。
InnoDB中的兩個表鎖:
意向共享鎖(IS):表示事務準備給數據行加入共享鎖,也就是說一個數據行加共享鎖前必須先取得該表的IS鎖
意向排他鎖(IX):類似上面,表示事務準備給數據行加入排他鎖,說明事務在一個數據行加排他鎖前必須先取得該表的IX鎖。
意向鎖是InnoDB自動加的,不需要用戶干預。
意向鎖是表級鎖
事務要獲取表A某些行的S鎖必須要獲取表A的IS鎖
事務要獲取表A某些行的X鎖必須要獲取表A的IX鎖
會話一鎖定了全表
?會話二,結果集為空時不加鎖
?
意向鎖的目的在于提高innodb性能,會話一鎖定的是全表,那么會話二一看全表已被鎖定,則不再去看每行是否鎖定
會話二先判斷表上有沒有表鎖,如果沒有表級鎖,則開始判斷有沒有行級鎖
會話一:不鎖定全表的S鎖
?會話二:立即執行,不被鎖;v1上有索引
?
v1字段上有索引,v2字段上沒有索引;有索引時,優先按有索引的規則來,當字段上沒有索引時,S鎖,X鎖走意向鎖的邏輯;
會話一
mysql> begin;select * from test where v1 >4 lock in share mode; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 5 | 5 | 3 | | 7 | 7 | 4 | | 10 | 9 | 5 | +----+------+------+ 3 rows in set (0.00 sec)mysql> rollback; Query OK, 0 rows affected (0.00 sec)mysql> begin;select * from test where v2 >4 lock in share mode; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 10 | 9 | 5 | +----+------+------+ 1 row in set (0.00 sec)
會話二:
mysql> begin;select * from test where v1 < 2 for update; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 1 | 1 | 0 | +----+------+------+ 1 row in set (0.00 sec)mysql> rollback; Query OK, 0 rows affected (0.00 sec)mysql> mysql> mysql> begin;select * from test where v2 < 2 for update; Query OK, 0 rows affected (0.00 sec)ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql>
如果不是S鎖,而是快照讀的話,不會走意向鎖的邏輯,因為快照讀不加鎖(不管是RC還是RR隔離級別);
會話一
mysql> begin;select * from test where v2 >4; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 10 | 9 | 5 | +----+------+------+ 1 row in set (0.00 sec)
會話二:立即執行,沒有鎖等待
mysql> begin;select * from test where v2 < 2 for update; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 1 | 1 | 0 | | 2 | 3 | 1 | +----+------+------+ 2 rows in set (0.00 sec)
?
對于無索引的情況,更新任何一條記錄,都會對該表加鎖,這時意向鎖將非常有用;但這個場景有個例外-->“半一致性讀”
半一致性讀的條件:
5.7及以下版本時,需要innodb_locks_unsafe_for_binlog 開啟或事務隔離級別為RC,語言類型為update
8.0版本,語言類型為update,事務隔離級別為RC;innodb_locks_unsafe_for_binlog 參數被廢棄。
8.0中半一致性讀測試
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
會話一
mysql> begin;select * from test where v2 >4 for update; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 10 | 9 | 5 | +----+------+------+ 1 row in set (8.77 sec)
會話二:顯式開始一個事務被阻塞,直接使用update語句不被阻塞;注意v2字段上無索引
mysql> begin;select * from test where v2 < 2 for update; Query OK, 0 rows affected (0.00 sec)^C^C -- query aborted ERROR 1317 (70100): Query execution was interruptedmysql> select * from test; +----+------+------+ | id | v1 | v2 | +----+------+------+ | 1 | 1 | 0 | | 2 | 3 | 1 | | 3 | 4 | 2 | | 5 | 5 | 3 | | 7 | 7 | 4 | | 10 | 9 | 5 | +----+------+------+ 6 rows in set (0.00 sec)mysql> update test set v2 = 1 where v2 < 2; Query OK, 1 row affected (0.00 sec) Rows matched: 2 Changed: 1 Warnings: 0mysql> exit
8.0的RR隔離級別
mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
會話一:X鎖測試
mysql> begin;select * from test where v2 >4 for update; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 10 | 9 | 5 | +----+------+------+ 1 row in set (0.00 sec)
?
會話二:
mysql> update test set v2 = 1 where v2 < 2; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
8.0中半一致讀的條件:RC及以下隔離級別,update語句
對于RR隔離級別,無索引的情況下,S鎖,X鎖,走意向鎖的邏輯
會話一:S鎖測試
mysql> begin;select * from test where v2 >4 lock in share mode; Query OK, 0 rows affected (0.00 sec)+----+------+------+ | id | v1 | v2 | +----+------+------+ | 10 | 9 | 5 | +----+------+------+ 1 row in set (0.00 sec)
會話二:
mysql> update test set v2 = 1 where v2 < 2; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
所以意向鎖的關鍵在于是否是S鎖與X鎖;如果是RC隔離級別,需要注意一下“半一致性讀”
S鎖會先有全表上加IS,X鎖會先在全表上加IX;IS與IX互斥,IX與IX及IS互斥;
對于經常使用的RR隔離級別,對于無索引字段,意向鎖減少了后來鎖判斷行記錄上是否有鎖的時間
?