大家好呀,我是樓仔。
對于這個問題,我 4 年前就專門研究過,最近看到網上很多相關的文章,要么總結得不全,要么存在很多問題。
感覺有必要自己寫一篇,一方面對網上的知識進行糾偏,另一方面也想全面總結一下這塊知識,方便大家學習。
這篇文章應該是全網總結最全的,如果有發現比我這篇寫得更好,更全,一定要私我哈。
不 BB,上文章目錄:
圖片
在驗證之前,我們先準備好具體的環境和數據,事務隔離級別 RR,數據庫版本 5.7.26。
為了方便測試,索引都是整型:
CREATE TABLE user ( id int(11) unsigned NOT NULL AUTO_INCREMENT, user_no int(11) NOT NULL COMMENT '用戶編號', user_name varchar(16) DEFAULT NULL COMMENT '用戶名', age int(3) DEFAULT NULL COMMENT '年齡', PRIMARY KEY (id), UNIQUE KEY un_idx_user_no (user_no), KEY idx_age (age)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
初始化數據:
insert into user values(1, 10, '樓仔', 18);insert into user values(4, 15, '二哥', 28);insert into user values(8, 20, '一灰', 38);
常用命令操作:
> start transaction; // 開啟事務> commit; // 提交事務> rollback; // 回滾事務> select @@transaction_isolation; // 查看事務隔離級別> select @@version; // 查看數據庫版本> SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; // 查詢鎖
說明:主鍵查詢,查詢數據存在。
執行悲觀鎖查詢:
select * from user where id = 1 for update;
執行更新操作,被鎖住了:
update user set user_name = "樓仔小弟" where id = 1;ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
查看鎖信息:
圖片
結論:查詢條件為主鍵,且有值,行鎖
操作:主鍵查詢,查詢數據不存在。
執行悲觀鎖查詢:
select * from user where id = 2 for update;
執行插入操作,被鎖住了:
insert into user values(3, 14, '樓仔小弟', 28);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
這里的間隙鎖,鎖住的區間是 id 字段的 (1,4) 區間,查看鎖信息:
圖片
結論:查詢條件為主鍵,且空值,間隙鎖
說明:唯一索引查詢,數據存在。
執行悲觀鎖查詢:
select * from user where user_no = 10 for update;
執行更新操作,被鎖住了:
update user set user_name = "樓仔小弟" where user_no = 10;ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
圖片
結論:查詢條件為唯一索引,且有值,行鎖
說明:唯一索引查詢,數據不存在。
執行悲觀鎖查詢:
select * from user where user_no = 11 for update;
執行插入操作,被鎖住了:
insert into user values(3, 14, '樓仔小弟', 28);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
這里的間隙鎖,鎖住的區間是 user_no 字段的 (1,4) 區間。
圖片
結論:查詢條件為唯一索引,且空值,間隙鎖
說明:普通索引,數據存在。
執行悲觀鎖查詢:
select * from user where age = 18 for update;
執行更新操作,被鎖住了:
update user set user_name = "樓仔小弟" where age = 18;ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
圖片
執行插入操作,被鎖住了:
insert into user values(3, 14, '樓仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
圖片
這里鎖住的是 age 字段的 [18, 28) 這區間。
結論:查詢條件為普通索引,且有值,間隙鎖
說明:普通索引,數據不存在。
執行悲觀鎖查詢:
select * from user where age = 19 for update;
執行插入操作,被鎖住了:
insert into user values(3, 14, '樓仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
圖片
這里鎖住的是 age 字段的 (18, 28) 這區間。
結論:查詢條件為普通索引,且空值,間隙鎖
說明:這里的索引,包括主鍵索引、唯一索引和普通索引。
執行悲觀鎖查詢:
select * from user where id > 1 for update;
執行插入操作,被鎖住了:
insert into user values(3, 14, '樓仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
圖片
這里其實可以對 id = 1 的數據進行更新,對于其它數據,都被鎖住,鎖住的范圍是 id 字段的 (1, 4],(4, 8],(8, 正無窮) 區間。
結論:查詢條件為索引,且是范圍查詢,間隙鎖。
執行悲觀鎖查詢:
select * from user where user_name = "樓仔" for update;
執行插入操作,被鎖住了:
insert into user values(3, 14, '樓仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
這里明顯是鎖表了,但是為什么鎖的信息還是行鎖呢,知道的同學,可以私我哈~~
結論:查詢條件為無索引,表鎖。
我們把上面的結論進行匯總:
圖片
總結如下規律:
那是否有一套加鎖規則呢?
為了便于大家理解,我先普及 3 個概念:
其實 MySQL 大佬林曉斌在極客時間講過,后來也有很多博主轉發過他的加鎖規則,我直接把這套規則貼一下。
兩個“原則”:
兩個“優化”:
這里我們結合上面的案例,來解讀這套加鎖規則。
針對我們前面總結的 5 條規律,我們先分析這兩條:
下面我們根據 “兩個原則” + “兩個優化” 來分析一下。
根據 “原則 1”,加鎖的基本單位是 next-key lock,當 “索引上為等值查詢” 時(即能查到該數據),根據 “優化 1”,間隙鎖退化為行鎖。
同理,根據 “優化 2”,索引上的等值查詢,向右遍歷時且最后一個值不滿足等值條件的時候,next-key lock 退化為間隙鎖。
再分析這兩條:
同上,通過 “原則 1” 和 “優化 2”,普通索引 是加的間隙鎖。
對于范圍查詢,個人認為上面的規則還不能完全覆蓋,當時林曉斌針對這些規則,舉了 4 個示例,然后進行詳細剖析,包括間隙鎖的范圍區間計算。
因為篇幅原因,這里就不再詳細展開,如果后續需要,我也可能會單獨出一篇。
最后我們再回顧一下(RR 隔離級別):
至于間隙鎖的范圍,如何計算,本文沒有詳細闡述,但是上面的這些規則,就能基本滿足我們日常工作需要。
本文鏈接:http://www.www897cc.com/showinfo-26-17150-0.htmlselect...for update,表鎖?行鎖?間隙鎖?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 2023年WebAssembly 現狀