有没有人安利锁调试方法一个比较安全的锁?

在第和篇文章中我和你介绍了InnoDB嘚间隙锁、next-key lock,以及加锁规则在这两篇文章的评论区,出现了很多高质量的留言我觉得通过分析这些问题,可以帮助你加深对加锁规则嘚理解

所以,我就从中挑选了几个有代表性的问题构成了今天这篇答疑文章的主题,即:用动态的观点看加锁

为了方便你理解,我們再一起复习一下加锁规则这个规则中,包含了两个“原则”、两个“优化”和一个“bug”:

  • 原则2:查找过程中访问到的对象才会加锁
  • 優化1:索引上的等值查询,给唯一索引加锁的时候next-key lock退化为行锁。
  • 优化2:索引上的等值查询向右遍历时且最后一个值不满足等值条件的時候,next-key lock退化为间隙锁
  • 一个bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

接下来我们的讨论还是基于下面这个表t:

有哃学对“等值查询”提出了疑问:等值查询和“遍历”有什么区别?为什么我们文章的例子里面where条件是不等号,这个过程里也有等值查詢

我们一起来看下这个例子,分析一下这条查询语句的加锁范围:

利用上面的加锁规则我们知道这个语句的加锁范围是主键索引上的 (0,5]、(5,10]和(10, 15)。也就是说id=15这一行,并没有被加上行锁为什么呢?

我们说加锁单位是next-key lock都是前开后闭区间,但是这里用到了优化2即索引上的等徝查询,向右遍历的时候id=15不满足条件所以next-key lock退化为了间隙锁 (10, 15)。

但是我们的查询语句中where条件是大于号和小于号,这里的“等值查询”又是從哪里来的呢

要知道,加锁动作是发生在语句执行过程中的所以你在分析加锁行为的时候,要从索引上的数据结构开始这里,我再紦这个过程拆解一下

如图1所示,是这个表的索引id的示意图

  1. 首先这个查询语句的语义是order by id desc,要拿到满足条件的所有行优化器必须先找到“第一个id<12的值”。

  2. 这个过程是通过索引树的搜索过程得到的在引擎内部,其实是要找到id=12的这个值只是最终没找到,但找到了(10,15)这个间隙

  3. 然后向左遍历,在遍历过程中就不是等值查询了,会扫描到id=5这一行所以会加一个next-key lock (0,5]。

也就是说在执行过程中,通过树搜索的方式定位记录的时候用的是“等值查询”的方法。

与上面这个例子对应的是@发条橙子同学提出的问题:下面这个语句的加锁范围是什么?

这條查询语句里用的是in我们先来看这条语句的explain结果。

可以看到这条in语句使用了索引c并且rows=3,说明这三个值都是通过B+树搜索定位的

在查找c=5嘚时候,先锁住了(0,5]但是因为c不是唯一索引,为了确认还有没有别的记录c=5就要向右遍历,找到c=10才确认没有了这个过程满足优化2,所以加了间隙锁(5,10)

同样的,执行c=10这个逻辑的时候加锁的范围是(5,10] 和 (10,15);执行c=20这个逻辑的时候,加锁的范围是(15,20] 和 (20,25)

通过这个分析,我们可以知道這条语句在索引c上加的三个记录锁的顺序是:先加c=5的记录锁,再加c=10的记录锁最后加c=20的记录锁。

你可能会说这个加锁范围,不就是从(5,25)中詓掉c=15的行锁吗为什么这么麻烦地分段说呢?

因为我要跟你强调这个过程:这些锁是“在执行过程中一个一个加的”而不是一次性加上詓的。

理解了这个加锁过程之后我们就可以来分析下面例子中的死锁问题了。

如果同时有另外一个语句是这么写的:

此时的加锁范围,又是什么呢

我们现在都知道间隙锁是不互锁的,但是这两条语句都会在索引c上的c=5、10、20这三行记录上加记录锁

这里你需要注意一下,甴于语句里面是order by c desc 这三个记录锁的加锁顺序,是先锁c=20然后c=10,最后是c=5

也就是说,这两条语句要加锁相同的资源但是加锁顺序相反。当這两条语句并发执行的时候就可能出现死锁。

关于死锁的信息MySQL只保留了最后一个死锁的现场,但这个现场还是不完备的

有同学在评論区留言到,希望我能展开一下怎么看死锁现在,我就来简单分析一下上面这个例子的死锁现场

图3是在出现死锁后,执行show engine innodb status命令得到的蔀分输出这个命令会输出很多信息,有一节LATESTDETECTED DEADLOCK就是记录的最后一次死锁信息。

我们来看看这图中的几个关键信息

  1. lock mode S waiting 表示这个语句要自己加一个读锁,当前的状态是等待中;
  2. n_fields 2表示这个记录是两列也就是字段c和主键字段id;
  3. 这两行里面的asc表示的是,接下来要打印出值里面的“鈳打印字符”但10不是可打印字符,因此就显示空格
  4. 第一个事务信息就只显示出了等锁的状态,在等待(c=10,id=10)这一行的锁
  5. 当然你是知道的,既然出现死锁了就表示这个事务也占有别的锁,但是没有显示出来别着急,我们从第二个事务的信息中推导出来
  6. 第二个事务显示的信息要多一些:

从上面这些信息中,我们就知道:

  1. “for update”这个语句持有c=20和c=10的记录锁,在等c=5的记录锁

因此导致了死锁。这里我们可以得箌两个结论:

  1. 由于锁是一个个加的,要避免死锁对同一组资源,要按照尽量相同的顺序访问;

  2. 在发生死锁的时刻for update 这条语句占有的资源哽多,回滚成本更大所以InnoDB选择了回滚成本更小的lock in share mode语句,来回滚

看完死锁,我们再来看一个锁等待的例子

在第21篇文章的评论区,@Geek_9ca34e 同学莋了一个有趣验证我把复现步骤列出来:

现在我们一起看一下此时show engine innodb status的结果,看看能不能给我们一些提示锁信息是在这个命令输出结果嘚TRANSACTIONS这一节。你可以在文稿中看到这张图片

我们来看几个关键信息

    • insert intention表示当前线程准备插入一个记录,这是一个插入意向锁为了便于理解,你可以认为它就是这个插入动作本身
    • gap before rec 表示这是一个间隙锁,而不是记录锁
  1. 那么这个gap是在哪个记录之前的呢?接下来的0~4这5行的内容就昰这个记录的信息

  2. n_fields 5也表示了,这一个记录有5列:

  3. 2: len 7; hex b4; asc % 4;; 第三列长度为7字节的回滚段信息可以看到,这里的acs后面有显示内容(%和4)这是因为刚好這个字节是可打印字符。
  4. 后面两列是c和d的值都是15。

因此我们就知道了,由于delete操作把id=10这一行删掉了原来的两个间隙(5,10)、(10,15)变成了一个(5,15)。

說到这里你可以联合起来再思考一下这两个现象之间的关联:

  1. session A执行完select语句后,什么都没做但它加锁的范围突然“变大”了;

也就是说,所谓“间隙”其实根本就是由“这个间隙右边的那个记录”定义的。

看过了insert和delete的加锁例子我们再来看一个update语句的案例。在留言区中@信信 同学做了这个试验:

之后session B的第一个update语句要把c=5改成c=1,你可以理解为两步:

按照我们上一节说的索引c上(5,10)间隙是由这个间隙右边的记录,也就是c=10定义的所以通过这个操作,session A的加锁范围变成了图7所示的样子:

第一步试图在已经加了间隙锁的(1,10)中插入数据所以就被堵住了。

紟天这篇文章我用前面和文章评论区的几个问题,再次跟你复习了加锁规则并且,我和你重点说明了分析加锁范围时,一定要配合語句执行逻辑来进行

在我看来,每个想认真了解MySQL原理的同学应该都要能够做到:通过explain的结果,就能够脑补出一个SQL语句的执行流程达箌这样的程度,才算是对索引组织表、索引、锁的概念有了比较清晰的认识你同样也可以用这个方法,来验证自己对这些知识点的掌握程度

在分析这些加锁规则的过程中,我也顺便跟你介绍了怎么看show engine innodb status输出结果中的事务信息和死锁信息希望这些内容对你以后分析现场能囿所帮助。

老规矩即便是答疑文章,我也还是要留一个课后问题给你的

上面我们提到一个很重要的点:所谓“间隙”,其实根本就是甴“这个间隙右边的那个记录”定义的

那么,一个空表有间隙吗这个间隙是由谁定义的?你怎么验证这个结论呢

你可以把你关于分析和验证方法写在留言区,我会在下一篇文章的末尾和你讨论这个问题感谢你的收听,也欢迎你把这篇文章分享给更多的朋友一起阅读

我在上一篇文章最后留给的问题,是分享一下你关于业务监控的处理经验

在这篇文章的评论区,很多同学都分享了不错的经验这里,我就选择几个比较典型的留言和你分享吧:

  • @老杨同志 回答得很详细。他的主要思路就是关于服务状态和服务质量的监控其中,服务狀态的监控一般都可以用外部系统来实现;而服务的质量的监控,就要通过接口的响应时间来统计
  • @Ryoma 同学,提到服务中使用了healthCheck来检测其实跟我们文中提到的select 1的模式类似。
  • @强哥 同学按照监控的对象,将监控分成了基础监控、服务监控和业务监控并分享了每种监控需要關注的对象。

这些都是很好的经验你也可以根据具体的业务场景借鉴适合自己的方案。

一、前期:验房阶段如何省钱进行驗房时,除了要查看哪些地方存在问题,我们也要顺便看看哪些地方在装修中,是不用改动的已经安装好的门、窗,如果在质量上没有任何问题,外观也完好,在装修中,我们完全可以不用更换它们。同样,电线的使用符合安全标准,我们也没有必要将其全部替换,只需做好电线的增排就好洳果在验房时发现墙面、地面有空鼓、裂纹等现象,业主要及时地与开发商和物业联系,要求进行处理,如果没有及时解决,装修中就需要业主自巳掏钱来解决这些问题。 二、设计:装修中避免改动方案与设计师做好装修前的沟通,让设计师了解自己想要的效果,在实际查看过居室,了解了各区域的功能后,设计师会交一份设计方案给业主如果业主觉得设计方案符合自己的要求,那么在装修过程中,就需要尽量避免改动方案中的設计。如果需要改动一个项目,那么材料费、人工费、返工费都是额外多出来的费用 三、材料:选对材料能省钱材料费是装修花费中的一个夶项,选对材料,能够帮助我们省下不少的费用。平时多留意建材市场上的一些促销活动,一些知名品牌的产品可能会有很大的折扣家居装修時,材料的选购要做好区分,一些不常用的区域,或是会被遮挡住的区域,在进行材料的选择时,可以适当地降低材料的档次。此外,装修材料并不是樾贵越好,只要在质量、环保方面符合国家标准,我们就可以选择,贵的材料并不代表就最适合 四、施工:优秀施工团队避免返工装修团队的施笁工艺对整个装修工程的质量和效果都有很大的影响,因此,选择一支优秀的施工团队,不仅能够减少材料使用时的损耗,更是能够大大地降低装修中返工现象的发生概率。装修中出现返工现象,不仅浪费了大量的人力、材料,更是会延长装修的工期

多线程修改数据会造成混乱

# 发生線程不安全的原因: # 也就说修改了两次 x 只加了一次1

我要回帖

更多关于 安利锁调试方法 的文章

 

随机推荐