MySQL锁(四)行锁的加锁规则和案例
在上一篇文章,我们学习了间隙锁和next-key lock,但是不知道怎么加锁,有哪些规则。间隙锁的概念不太好理解,尤其是配合上行锁后,很容易在判断是否会出现锁等待的问题上犯错。
今天我们就来学习一下加锁规则吧。
在学习前要说明一点,以下的规则只限于版本范围:5.x系列<=5.7.24,8.0系列<=8.0.13。
加锁规则
这个加锁规则包含两个“原则”、两个“优化”和一个“bug”。
- 原则1:加锁的基本单位是next-key lock。希望你还记得,next-key lock是前开后闭区间。
- 原则2:查找过程中访问到的对象才会加锁。
- 优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。
- 优化2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。
- 一个bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。
下面以表t为例来介绍一下这些规则。表t的建表语句和初始化语句如下。
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);
案例1:等值查询间隙锁
图1 等值查询间隙锁
分析:
- 步骤1:根据原则1,加锁(5, 10]
- 步骤2:根据优化2,id=10不满足查询条件,因此退化为间隙锁(5, 10)
结论:
- Session B阻塞是因为id=8在间隙锁(5, 10)内
- Session C可以执行是因为没有锁住id=10这行
案例2:非唯一索引等值锁
图2 非唯一索引等值锁
分析:
- 步骤1:根据原则1,加锁(0, 5]
- 步骤2:由于c是普通索引,因此还需要继续遍历,直到找到c=10,不满足条件。根据原则2,访问到的对象要加锁,因此要加(5, 10]。
- 步骤3:同时根据优化2,这是一个等值查询,向右遍历的不满足条件的第一个值10,(5, 10]要退化为间隙锁(5, 10)
- 因此加锁是索引c的next-key lock(0,5]和间隙锁(5,10)
结论:
- Session B可以执行是因为加锁的是索引c,而不是主键索引
- Session C阻塞是因为c的插入值是7,在间隙锁(5, 10)范围内
案例3:主键索引范围锁
图3 主键索引范围锁
分析:
- 步骤1:根据原则1,加锁(5, 10]
- 步骤2:根据优化1,退化为id=10的行锁
- 步骤3:继续遍历,找到不满足id<11的值id=15,加锁(10, 15]
因此加锁id=15的行锁和id的next-key lock(10, 15]
结论:
- 插入id=13被阻塞:next-key lock (10, 15]
- 更新id=15被阻塞:next-key lock (10, 15]
案例4:非唯一索引范围锁
图4 非唯一索引范围锁
分析:
- 步骤1:根据原则1,加锁(5, 10]
- 步骤2:继续遍历,找到不满足c<11的值c=15,根据原则2,加锁(10, 15]
因此加锁索引c (5, 10]和(10, 15]
结论:
- 插入c=8,被(5, 10]阻塞
- 更新c=15,被(10, 15]阻塞
案例5:唯一索引范围锁bug
图5 唯一索引范围锁bug
分析:
- 步骤1:根据原则1,加锁(10, 15],再根据优化1,退化为id=15的行锁
- 步骤2:向右遍历,加锁(10, 15]
- 步骤3:根据BUG,要访问到不满足条件的第一个值,即id=20,加锁(15 ,20]。
因此加锁为(10, 15]和(15, 20]
结论:
- 更新id=20阻塞,被(15, 20]锁住
- 插入id=16阻塞,被(15, 20]锁住
案例6:非唯一索引上存在"等值"的例子
mysql> insert into t values(30,10,30);
图6 非唯一索引上存在"等值"的例子
分析:
- 步骤1:根据原则1,(c=5,id=5)到(c=10,id=10)这个next-key lock
- 步骤2:向右查找,直到碰到(c=15,id=15)这一行,循环才结束。根据优化2,这是一个等值查询,向右查找到了不满足条件的行,所以会退化成(c=10,id=10) 到 (c=15,id=15)的间隙锁
因此加锁(c=5,id=5)到(c=10,id=10)这个next-key lock和(c=10,id=10)到(c=15,id=15)这个间隙锁
结论:
- 插入c=12阻塞,被(c=10,id=10)到(c=15,id=15)这个间隙锁锁住
- 更新c=15成功,没有锁住c=15
案例7:limit 语句加锁
先插入一条记录。
mysql> insert into t values(30,10,30);
图7 limit 语句加锁
分析:
- 步骤1:根据原则1,(5, 10],因为c=10有两条行,因此遍历到这里就结束
- 步骤2:因为是delete,因此加两个行锁(id=10和id=30)
因此加锁c (5, 10)和两个行锁(id=10和id=30)
结论:
- 插入c=12成功,因为c=12没有被锁住
说明:这个例子对我们实践的指导意义就是,在删除数据的时候尽量加limit。
案例8:一个死锁的例子
这个案例的目的是说明:next-key lock实际上是间隙锁和行锁加起来的结果。
图8 一个死锁的例子
分析:
- 步骤1:根据原则1,加锁(5, 10]
- 步骤2:继续遍历,直到c=15不满足条件,加锁(10, 15],根据优化2,退化为(10, 15)
结论:
- Session B在等待锁,此时Session B已经加了间隙锁(5, 10),在等待加行锁c=10。
- Session A插入c=8,也在等待锁,从而导致死锁
说明:next-key lock具体执行的时候,是要分成间隙锁和行锁两段来执行的。
案例9:非唯一索引排序范围锁
图9 非唯一索引排序范围锁
分析:
- 步骤1:先执行c=20,加锁(15, 20]
- 步骤2:根据优化2,加间隙锁(20, 25)
- 步骤3:再执行c=15,加锁(10, 15]
- 步骤4:继续向左遍历,找到记录id=10为止,加锁(5, 10]
在扫描过程中,c=20、c=15、c=10这三行都存在值,由于是select *,所以会在主键id上加三个行锁。
因此要加锁索引c (5, 25)和三个行锁(id=10,id=15,id=20)。
结论:
- 插入c=6,被c(5, 25)锁住
案例10:不等号条件里的等值查询
begin;
select * from t where id>9 and id<12 order by id desc for update;
在执行过程中,通过树搜索的方式定位记录的时候,用的是“等值查询”的方法。
分析:
- 步骤1:根据原则1,加锁 (10, 15]
- 步骤2:根据优化2,退化为(10, 15)
- 步骤3:向左遍历,找到id=10,加锁(5, 10],继续找到id=5为止,加锁(0, 5]
案例11:in范围锁
begin;
select id from t where c in(5,20,10) lock in share mode;
分析:
说明:锁是逐个逐个加的。
- 步骤1:先c=5,加(0, 5]和(5, 10)
- 步骤2:再c=10,加(5, 10]和(10, 15)
- 步骤3:后c=20,加(15, 20]和(20, 25)
间隙锁是不互斥的,因为加锁范围是(0, 25),除c=15外。
死锁情况
select id from t where c in(5,20,10) order by c desc for update;
有一种情况,同时执行倒序语句,因为刚好同时执行,逐渐加锁(倒序加锁),会出现死锁情况。
参考资料
MySQL锁(四)行锁的加锁规则和案例的更多相关文章
- MySQL中的锁(表锁、行锁)
锁是计算机协调多个进程或纯线程并发访问某一资源的机制.在数据库中,除传统的计算资源(CPU.RAM.I/O)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所在有数 ...
- Mysql表锁、行锁、页锁
参考 http://www.jb51.net/article/50047.htm <MySQL行级锁.表级锁.页级锁详细介绍> 页级:引擎 BDB.表级:引擎 MyISAM , 理解为锁住 ...
- [转]MySQL 表锁和行锁机制
本文转自:http://www.cnblogs.com/itdragon/p/8194622.html MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整 ...
- 悲观锁,乐观锁,排他锁,行锁----MYSQL
在说具体的锁结构时,先思考一个问题,那就是为什么要上锁?然后我要如何选择锁?锁具体如何实现? 在文章得末尾我给出了我的个人答案. 一.什么是悲观锁? 1.悲观锁就是在操作数据时,认为此操作会出现数据冲 ...
- MySql中的锁(表锁,行锁)
锁是计算机协调多个进程或春线程并发访问某一资源的机制.在数据库中,除传统的计算资源(CPU,RAM,I/O)的争用之外,数据也是一种工许多用户共享的资源.如何保证数据并发访问的一致性,有效性是所有数据 ...
- MySQL表锁和行锁
锁粒度 MySQL 不同的存储引擎支持不同的锁机制,所有的存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的锁实现: InnoDB 存储引擎既支持行级锁(row-level locki ...
- MySQL的中的全局锁、表级锁、行锁
MySQL的中的全局锁.表级锁.行锁 学习极客时间-林晓彬老师-MySQL实战45讲 学习整理 全局锁 对整个数据库实例加锁.通过使用Flush tables with read lock (FTWR ...
- mysql的innodb 引擎 表锁与行锁
innodb 引擎 行锁与表锁 行锁与表锁是基于索引来说的(且索引要生效) 不带索引 (表锁)要全表扫描 1. 执行select @@autocommit; 查看结果 0是不自动提交事务,1是自动提交 ...
- innodb 表锁和行锁
表锁 表锁相关结构: table->locks:数据字典table保存这个表上的所有表锁信息 trx->lock.table_locks:每个事务trx保存该事务所加的所有表锁信息 tr ...
随机推荐
- 深度分析:理解Java中的多态机制,一篇直接帮你掌握!
Java中的多态 1 多态是什么 多态(Polymorphism)按字面的意思就是"多种状态".在面向对象语言中,接口的多种不同的实现方式即为多态.用白话来说,就是多个对象调用同一 ...
- 13.java设计模式之模板模式
基本需求: 制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎 通过添加不同的配料,可以制作出不同口味的豆浆 选材.浸泡和放到豆浆机打碎这几个步骤对于制作每种口味 ...
- 思维导图软件iMindMap怎么用模板制作思维导图
随着思维导图的不断发展,市场上相关的软件也越来越多.像XMind.MindManager等.每一款软件都有它独特的亮点.作为众多思维导图软件中的一款,iMindMap算是比较亮眼的了.现在很多人都在用 ...
- java运算符与程序逻辑控制
一.运算符 java中的运算符大致分为四种:数学运算符,关系运算符.逻辑运算符.位运算 1.数学运算符:即咱们平常说的加减乘除运算,这种运算是分先后顺序的,如果想要优先进行运算,建议加上小括号,使其运 ...
- Oracle数据库由dataguard备库引起的log file sync等待
导读: 最近数据库经常出现会话阻塞的报警,过一会又会自动消失,昨天晚上恰好发生了一次,于是赶紧进行了查看,不看不知道,一看吓一跳,发现是由dataguard引起的log file sync等待.我们知 ...
- iOS如何实现语音播报及后台播放
最近项目刚刚交付,偶然间用到了语音播报和语音搜索的功能.语音搜索我用的是讯飞的demo,感觉效果还不错,感兴趣的话可以去官网上面下载demo,里面讲的特别的详细,不过稍显麻烦一些.语音播报讯飞也有de ...
- Jinja2语法自动补全配置
Jinja2语法自动补全配置 说明 在使用Pycharm社区版进行Web开发时,Jiaja2的语法是不会自动提示补全的,为了提高开发效率,需要根据个人习惯进行一些常用语法的自动补全配置,具体如下. 配 ...
- 公平lock和非公平lock的区别
可以看到区别在于,在lock时和tryAquire时,非公平锁不会去管队列中有没有线程在排队,直接尝试去获取锁,失败之后就和公平锁一样,乖乖去排队. 也就是说发生竞争的场景在于,尚未入队的线程之间和刚 ...
- LeetCode 037 Sudoku Solver
题目要求:Sudoku Solver Write a program to solve a Sudoku puzzle by filling the empty cells. Empty cells ...
- 面试官:说一下List排序方法
1. 前言 排序算是比较高频的面试题了,节前面试了的两家公司都有问到排序问题,整理后分享给大家(文末见总结). 通常我们想到实现排序就是 Collections 工具类的 sort() 方法,而 so ...