鎖的類型 https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
測試表
CREATE TABLE `dev` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
`x` int NOT NULL DEFAULT '0' COMMENT 'x',
`y` int NOT NULL DEFAULT '0' COMMENT 't',
`z` int NOT NULL DEFAULT '0' COMMENT 'z',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_z` (`z`),
KEY `idx_y` (`y`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='table';
測試數(shù)據(jù)
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (2, 22, 222),(4, 44, 444),(6, 66, 666);
版本
select version(); // 輸出:8.0.25
select @@SESSION.transaction_isolation; //輸出:REPEATABLE-READ
現(xiàn)象1(記錄存在的等值查詢):
session 1
begin;
select * from dev where y = 44 for update;
session 2
begin;
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (1, 11, 111); // 成功寫入
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (1, 22, 200); // 輸出: Lock wait timeout exceeded; try restarting transaction
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (4, 44, 400);// 輸出: Lock wait timeout exceeded; try restarting transaction
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (5, 55, 555);// 輸出: Lock wait timeout exceeded; try restarting transaction
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (6, 66, 600); // 成功寫入
結(jié)論1:
鎖是加在索引上,算法為next-key lock,是一個左閉右開的區(qū)間,因此y=22行不能新寫入。其中y=44不是唯一索引,InnoDB 會繼續(xù)查找到下一個y不等于44的記錄,因?yàn)槭亲箝]右開,因此數(shù)據(jù)y=44不能新寫入,而y=66可以新寫入。
現(xiàn)象2(記錄存在的唯一索引的等值查詢):
session 1
begin;
select * from dev where z = 444 for update;
session 2
begin;
INSERT INTO `dev` (`x`, `y`, `z`) VALUES (3, 33, 333); // 成功寫入
結(jié)論2:
記錄存在的唯一索引的等值查詢,next-key lock會優(yōu)化為 record lock
現(xiàn)象3(記錄不存在的等值查詢)
select * from dev where y = 40 for update; // 按照之前的結(jié)論:鎖的區(qū)間應(yīng)該為 [22, 44)(注意是左閉右開),也就是寫入y=22,33是失敗的,y=44是成功的