0x01:什麼是間隙鎖
間隙鎖(Gap Lock)是Innodb在可重複讀送出下為了解決幻讀問題時引入的鎖機制。當用範圍條件而不是相等條件檢索資料,并請求共享或排他鎖時,InnoDB會給符合條件的已有資料記錄的索引項加鎖;對于鍵值在條件範圍内但不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這些“間隙”進行加鎖,這種鎖機制就是所謂的間隙鎖(NEXT-KEY)鎖。
0x02:間隙鎖引起的問題
因為執行SELECT語句中,如果通過範圍查找的話,間隙鎖會鎖定整個範圍内所有的索引鍵值,即使這個鍵值并不存在。這個就是間隙鎖最緻命的缺點,就是當鎖定一個範圍鍵值之後,即使某些不存在的鍵值也會被無辜的鎖定,而造成在鎖定的時候無法插入鎖定值範圍内的任何資料,在某些場景下這可能會針對性造成很大的危害。
0x03:間隙鎖例子
建表:
CREATE TABLE `gas_lock_tab` (`id` bigint(20) NOT NULL COMMENT 'id' ,`user_name_py` int(11) NOT NULL COMMENT '使用者姓名拼音' ,PRIMARY KEY (`id`),INDEX `usernameIndex` (`user_name_py`) USING BTREE )ENGINE=InnoDBDEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ciROW_FORMAT=DYNAMIC;
插入資料:
INSERT INTO `gas_lock_tab`(`id`, `user_name_py`) VALUES (1, 'huangjinjin');INSERT INTO `gas_lock_tab`(`id`, `user_name_py`) VALUES (3, 'java樂園');INSERT INTO `gas_lock_tab`(`id`, `user_name_py`) VALUES (5, '架構師知音');INSERT INTO `gas_lock_tab`(`id`, `user_name_py`) VALUES (8, 'java狂人');
插入資料後查詢:
mysql> select * from gas_lock_tab;+----+--------------+| id | user_name_py |+----+--------------+| 1 | huangjinjin || 3 | java樂園 || 8 | java狂人 || 5 | 架構師知音 |+----+--------------+4 rows in set
注意表中的資料,id字段是int型,包含1,3,5,8;當然1到8中間,缺少連續的id:2,4,6,7,而沒有連續下來。一般在表裡的主鍵id最好是連續的,友善索引;所謂的删除其實是做邏輯删除,隻是做了狀态更改,而不做實體删除。
打開兩個Mysql終端,分别設定autocommit為0(手動送出事務),也就是關閉自動送出功能,事務隔離級别處于可重複讀狀态。
session 1:
mysql> set autocommit=0;Query OK, 0 rows affected
session 2:
mysql> set autocommit=0;Query OK, 0 rows affected
session 1 執行update操作,執行成功
mysql> update gas_lock_tab set user_name_py = '1234' where id > 1 and id
也就是對3這四條資料做修改。注意這裡沒有id為2和4的記錄;在第二個終端執行insert操作,發現被阻塞。
insert into gas_lock_tab values (2,'it大佬');
按說在InnoDB的行級鎖,兩個不同的終端操作不同的行資料,不會造成阻塞,但是阻塞出現了。達到逾時時間後,seesion 2出現如下錯誤:
另外,如果在session 2的插入語句沒有逾時之前,對session 1進行 commit操作,則會發現session 2也會執行操作成功。
session 1 commit操作:
session 2出現的結果:
這時對session 1做commit操作,發現update和inster操作都生效了。