MySQL InnoDB支持三种行锁定方式:
- 行锁(Record Lock):锁直接加在索引记录上面。
- 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。
- Next-Key Lock:行锁与间隙锁组合起来用就叫做Next-Key Lock。
一. 间隙锁的概念
- 当我们用范围条件(between)而不是相等条件(=)检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;
- 对于键值在条件范围内但不存在的记录,叫做 “间隙(GAP)”,InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁(NEXT-KEY)锁。
二. 间隙锁的产生
1. 创建测试表
CREATE TABLE `test`.`Untitled` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`sex` int(255) NOT NULL,
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2. 进行sql语句查询
select id,name from t_student where id > 0 and id < 5 for update;
-
select ... for update
这条语句,是会对数据记录加锁的,这里因为命中了索引,加的是行锁
。从数据记录来看,这里排它锁锁住数据是id为1、3和4的这3条数据。 -
但是,看看前面我们的介绍——对于键值在条件范围内但不存在的记录,叫做"间隙(GAP)",InnoDB也会对这个"间隙"加锁。
-
id=2的记录insert进来了,一直保持等待(会出现超时等待问题),要等到这个事务结束以后才会执行的,
三. 间隙锁的作用
①. 防止幻读
时间 | 事务A | 事务B |
---|---|---|
T0 | BEGIN; | BEGIN; |
T1 | select count(1) from t_student where id > 1; | |
T2 | insert into t_student VALUES(2,‘戏子111’,1,“杭州”); | |
T3 | commit; | |
T4 | select count(1) from t_student where id > 1; | |
T5 | commit; |
事务A进行操作
事务B进行操作
事务A再进行操作
- 如果没有间隙锁,事务A在T1和T4读到的结果是不一样的,有了间隙锁,读的就是一样的了,解决了幻读的问题。
②. 防止数据误删/改
时间 | 事务A | 事务B |
---|---|---|
T0 | BEGIN; | BEGIN; |
T1 | delete from t_student where id < 4; | |
T2 | insert into t_student VALUES(2,‘戏子111’,1,“杭州”); | |
T3 | commit; | |
T4 | commit; |
- 这种情况下,如果没有间隙锁,会出现的问题是:id为2的记录,刚加进去,就被删除了,这种情况有时候对业务,是致命性的打击。加了间隙锁之后,由于insert语句要等待事务A执行完之后释放锁,避免了这种情况
三.使用间隙锁的隐患
- 事务A锁住了需要插入数据的区域,一直在进行数据处理,导致插入数据一直在等待,对插入的性能就有很大影响了,必须等到事务结束才能进行插入,性能会降低。