目录
这次我们来聊聊MySQL数据库中InnoDB引擎中的锁。
InnoDB引擎是支持事务和行锁的。那么什么是行锁呢。
行锁
什么是行锁
行锁就是每次锁定的是一行数据的锁机制,也就是行级别锁定(row-level)。
行锁的实现方式
InnoDB中的行锁是通过给索引上的索引项加锁来实现的。
重点:所以只有通过索引条件检索数据,InnoDB才能使用行级锁,否则将使用表锁。
注意事项
1. 因为行锁的实现方式是对索引上的索引项加锁,而不是针对记录,所以使用相同的索引键是会出现锁冲突的。比如索引的类型是Normal,而且值相同的情况下,对相同值进行加锁会导致锁冲突。
2. 不通过索引条件查询的时候,使用的是表锁不是行锁。
3. 表内有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行。
4. 即使SQL语句中使用了索引,但是仍然需要explain去分析MySQL是否使用索引去查询。因为MySQL会去判断使用索引和全表扫描哪个效率更高,而去决定查询方式。如果全表扫描的话则用的是表锁。
加锁规范
目前有user表,id是主键,name是普通索引,psw是普通字段无索引
行锁生效(默认是id = 3 存在 name = "3" 的数据存在,如果数据不存在就不会加锁)
SELECT * FROM user WHERE id=3 FOR UPDATE;
SELECT * FROM user WHERE name='3' FOR UPDATE;
SELECT * FROM user WHERE id=3 and psw='1' FOR UPDATE;
死锁的情况
比如两个事务
A 先锁定 user 表内的 id = 3 , 再去锁 name = "3"
B 先锁定name = "3" , 再去锁 id = 3。
那么这种情况就会出现死锁的情况。
解决办法:
1.MySQL是默认开启死锁检测的,检测到死锁之后会进行事务的回滚。
2. 如果禁用了死锁检测的话,可以设置innodb_lock_wait_timeout,进行超时回滚
3. 使用SHOW ENGINE INNODB STATUS 查询出现的最后一个死锁。也可以打印所有的死锁日志,自己去优化。
防范建议:
1. 建议事务不要时间太长。
2. 规范获取锁的流程
3. 等等。。。自己想去吧老弟嘿嘿嘿
行锁的类型
行锁一般分为排他锁,共享锁,意向锁。排他锁和共享锁优点类似于JAVA中的读写锁。
排他锁
排他锁其实也可以称为写锁。顾名思义,假如一个事务获取了一个数据行的排他锁,那么其他事务就不能再次去获取该行的其他锁,包括共享锁和排他锁。
重点:如果直接通过select * 不加锁的方式去获取数据的话,仍然是可以获取到数据的
加锁方式:
select * from ..... for update;
共享锁
共享锁则是类似于读锁,多个事务对同一个数据可以共享一把锁,但是只能读不能修改。
加锁方式:
select ..... lock in share mode;
表锁
意向锁
意向锁是一种不与行锁冲突的表级锁。意向锁是数据殷勤自己维护的,用户无法动手操作意向锁。
意向锁的存在是为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存
举例:事务A修改user表的记录r,会给记录r上一把行级的排他锁(X),同时会给user表上一把意向排他锁(IX),这时事务B要给user表上一个表级的排他锁就会被阻塞。意向锁通过这种方式实现了行锁和表锁共存且满足事务隔离性的要求
重点:意向锁中的共享锁和排他锁与行锁中的不冲突
1.意向共享锁
2.意向排他锁
间隙锁
间隙锁(Gap Lock)是Innodb在RR(可重复读)提交下为了解决幻读问题时引入的锁机制。
锁定索引记录间隙,确保索引记录的间隙不变。
左开右闭原则
即间隙锁的区间最左边的数值是不含在间隙锁内,右边是含在间隙锁内的。
例如(3,5],使用间隙锁之后,可以读取到3的信息而不能读到5。
间隙锁的使用条件
1. 必须在RR级别下
2. 检索条件必须使用索引
临键锁 Next-Key Lock
临键锁即(间隙锁 + 行锁)是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。