锁分类
- 按锁定粒度划分。分为行级锁、表级锁、间隙锁。
- 按排斥性划分。分为共享锁(S锁)、排他锁(X锁)。
- 按是否上锁划分。乐观锁、悲观锁
锁概念解释
- 行级锁 顾名思义:锁定的是单行记录。按照索引进行DDL的时候此时使用的是行级锁。
- 表级锁 顾名思义:锁定的是整个表。因为无法缩小查找范围,需要进行全表扫描。
- 间隙锁 在RR级别下会在一定情况下对部分操作使用间隙锁(参考下方)。会根据唯一索引的数据大小把数据划分为范围进行锁定。如id列的值为 1,3,5。则划分为 负无穷至1, 1至3, 3至5,5至正无穷(前开后闭原则,第二个包围包括3,但是不包括5),其他类推。此时执行update table set col = 1 where id = 3, 则会锁定住3至5的范围。进行insert into(4) 这个操作需要进行等待。 间隙锁的引入是为了解决幻读问题的。
- 共享锁(S锁)select in share mode使用共享锁。 共享锁会在读取完之后立即释放。
- 排他锁 (X锁) select for update、insert、update、delete 使用排他锁。 排他锁是在事务结束后释放。 正常的select 是没有锁的。采取mvcc的方式进行读取。
- 乐观锁 在查询的时候不进行添加索。然后再更新的时候通过版本号字段进行判断。 如先查询 select version from table where id = 1, 然后更新的时候把version当作更新条件 update table set version = version + 1, col = x where id = 1 and version = 之前查到的version。 业务上注意添加没更新成功时的判断逻辑
- 悲观锁 再一个事务中查询进行加索操作。 比如 select for update
语句加索情况分析
Update & Delete语句加锁(此段出处为阿里技术公众号)
1)聚簇索引(查询命中)
UPDATE students SET score = 100 WHERE id = 15;

RC、RR都是对聚簇索引加X锁。
2)聚簇索引(查询未命中)
UPDATE students SET score = 100 WHERE id = 16;

RC不加锁,RR在16之前和之后的范围里加GAP锁。
3)二级唯一索引(查询命中)
UPDATE students SET score = 100 WHERE no = 'S0003';

RC、RR会对二级和聚簇索引都加X锁(防止其他事务通过聚簇改数据)。
4)二级唯一索引(查询未命中)
UPDATE students SET score = 100 WHERE no = 'S0008';

RC不加锁,RR只在二级索引加GAP。
5)二级非唯一索引(查询命中)
UPDATE students SET score = 100 WHERE name = 'Tom';

RC对二级和聚簇加X锁,RR对二级加X锁和Gap对聚簇加X锁。
6)二级非唯一索引(查询未命中)
UPDATE students SET score = 100 WHERE name = 'John';

RC不加锁,RR只在二级索引加GAP。
死锁
死锁案例一

死锁案例二

25和26记录都不存在,A和B并没有更新任何记录,但是由于数据库隔离级别为RR,所以会在 (20, 30) 之间加上间隙锁。之后A和B分别执行 INSERT 要插入25和26,需要在 (20, 30) 之间加插入意向锁,插入意向锁和间隙锁冲突,所以两个事务互相等待,最后形成死锁。
死锁案例三

加锁是一条记录一条记录挨个加锁,如果两条 SQL 语句的加锁顺序不一样,也可能会导致死锁。A 的加锁顺序为:id = 20 -> 30,B 的加锁顺序为:id = 30 -> 20,正好相反,所以会导致死锁。
死锁如何处理
- 设置事务等待时间 innodb_lock_wait_timeout
- 规范编码 尽量保证事务中锁的顺序性,从而避免AB-BA问题。
- show full processlist 查看一直在等待的进程,然后kill
- wait for graph 可以判断当前是否发生了死锁
锁升级(innodb不存在锁升级)
跟mysql的锁定使用位图方式有关。锁并不是稀缺资源。
本文围绕MySQL数据库的锁展开,介绍了按锁定粒度、排斥性、是否上锁的锁分类,解释了行级锁、表级锁等锁概念。分析了Update & Delete语句在不同索引和查询命中情况下的加锁情况,列举了死锁案例并给出处理方法,还提到innodb不存在锁升级。
1675

被折叠的 条评论
为什么被折叠?



