本文在鎖概述的基礎上,一般實驗舉例,詳細地介紹了意向鎖的原理。mysql
鎖範圍 sql
全局鎖(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)spa
讀鎖
加讀鎖後,只能對錶讀,不能對錶寫;容許多個會話同時讀;其餘會話能夠加共享讀鎖
lock table table_name read設計
寫鎖
lock table table_name write
持有鎖的會話可寫可讀
其餘會話訪問表或請求加鎖都會被阻塞,直到鎖釋放版本控制
釋放鎖
unlock tables;
lock table 鎖不能相互嵌套,一個事務開始就意味着另一個事務結束
顯式開啓一個事務,由於事務中不能支持表鎖,因此事務開始則表鎖斷開
Kill或鏈接斷開rest
innodb鎖
默認爲行鎖
在索引上加鎖來實現行鎖
若是沒有索引,那麼升級爲全表記錄鎖,最終效果等同於表鎖;但表鎖只須要在根節點上加鎖,而不是對全部記錄加鎖,因此代價要小一些code
鎖類型
共享鎖
排他鎖/獨佔鎖
意向鎖,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鎖
上表描述幾種鎖之間的的關係,其中AI是自增鎖,表主健自增用到。
會話一鎖定了全表
會話二,結果集爲空時不加鎖
意向鎖的目的在於提升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 interrupted 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) mysql> update test set v2 = 1 where v2 < 2; Query OK, 1 row affected (0.00 sec) Rows matched: 2 Changed: 1 Warnings: 0 mysql> 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隔離級別,對於無索引字段,意向鎖減小了後來鎖判斷行記錄上是否有鎖的時間