前几天线上收到一条告警邮件,生产环境MySQL操作发生了死锁,邮件告警的提炼出来的SQL大致如下。

update pe_order_product_info_test
set end_time = '2021-04-30 23:59:59'
where order_no = '111111111'
and product_id = 123456
and status in (1,2);
update pe_order_product_info_test
set end_time = '2021-04-30 23:59:59'
where order_no = '222222222'
and product_id = 123456
and status in (1,2);

是一条Update语句,定位了它的调用情况,发现Update的调用方只有一处,并且在Cat中看到一个小时的调用次数只有700多次,这个调用量基本与并发Update引起死锁无关了。

当时猜测了几种情况,这里Update进行操作时有其他业务方调用Select相关的接口,但是排查了那个时间点发生死锁应用的调用链,发现好像并没有其他会影响到Update的调用。

为了更进一步了解当时的情况,就联系了DBA老师,要了当时死锁发生时的日志,准备拿到日志之后大干一场,好好分析一下问题,结果...

DBA老师看了死锁日志直接点出了问题要害——index_merge索引合并。

1. 什么是索引合并

这是MySQL在5.1引入的优化技术,再此之前,一个表仅仅只能使用一个索引,但索引合并的引入,可以对同一张表使用多个索引分别进行条件扫描。

如果要拿索引合并index_merge与只使用一个索引做比较,那么拿上面那个update语句来做演示。

update pe_order_product_info_test
set end_time = '2021-04-30 23:59:59'
where order_no = '111111111'
and product_id = 123456
and status in (1,2);

只是用一个索引时,MySQL会选择一个最优的索引来使用,比如使用index_order_no,拿它来找出所有order_no为111111111的索引记录,从该索引上找到它的PRIMARY索引的id,然后回表找到对应的行数据,最后在内存中根据剩下的product_id和status条件来进行过滤。

但如果MySQL优化器觉得你如果只是用一个索引,拿出大量记录,然后再在内存中使用product_id和status过滤(并且符合该条件的记录值很少),这个第二步效率可能不高时,他就会使用索引合并进行优化。

如果使用索引合并去判断where条件时,那么它就会先通过index_order_no索引去找到PRIMARY索引的id,再通过index_product_id索引去找到PRIMARY索引的id,最后将两个id集合求交集,再回表找到行数据。(索引合并使用索引的顺序是不确定的)

2. 场景复现

在MySQL的Bug反馈文档中也有记录一个Bug #77209的记录,标注了索引合并引发死锁的情况。但是我按照它给出的repeat并不能重现索引合并的场景,在它的实例中早了600万随机数,我猜测可能是MySQL调高了索引合并的条件,将数据量增加到了1000万。

先来带大家复现一下当时的情况。

环境:MySQL 5.6.24

  1. 创建一张测试表

    CREATE TABLE `a` (
    `ID` int AUTO_INCREMENT PRIMARY KEY,
    `NAME` varchar(21),
    `STATUS` int,
    KEY `NAME` (`NAME`),
    KEY `STATUS` (`STATUS`)
    ) engine = innodb;
  2. 导入数据,为了方便导入一些随机数据,需要先开启一个兼容性配置。

    set global show_compatibility_56=on;

    开始导入随机数据。

    set @N=0;
    insert into a(ID,NAME,STATUS)
    select
    @N:=@N+1,
    @N%1600000,
    floor(rand()*4)
    from information_schema.global_variables a, information_schema.global_variables b, information_schema.global_variables c
    LIMIT 10000000;
  3. 测试

    update a set status=5 where rand() < 0.005 limit 1;
    explain UPDATE a SET STATUS = 2 WHERE NAME = '1000000' AND STATUS = 5;

3. 为什么发生了死锁

直接上一副图,以及两个update事务的加锁流程。

可以看到在订单与产品这个模型中,Update事务一和Update事物二在product_id索引和primary索引上都存在交叉重合,这就导致了死锁的发生。

步数 事务一 事务二
1 锁住index_order_no索引树上order_no为2222的索引项
2 锁住index_order_no索引树上order_no为3333的索引项
3 回表锁住 PRIMARY 索引中 id 为 11 的索引项
4 回表锁住 PRIMARY 索引中 id 为 12 的索引项
5 锁住index_product_id索引树上product_id为2000的四个索引项
6 尝试去锁住index_product_id索引树上product_id为2000的四个索引项,但是已经被事务一锁住,等待事务一释放index_product_id上的锁
7 试图回表锁住 PRIMARY 索引中 id 为10,11,12,13的索引项,发现id为12的索引项在第4步已经被事务二锁住,等待事务二释放

这就是本次死锁发生的原因所在了,解决方案有很多种,可以根据具体场景选择。

  1. 删除某一个索引,这当然不是一个好办法
  2. 关闭index_merge优化
  3. 为查询条件增加联合索引,在本例中是product_id和order_no。

4. 最后

当然最后这些都是我个人的分析,DBA老师给的建议是直接上联合索引,网上关于索引合并的资料实在太少了,除了官方文档简单扯了扯,剩下的都是转载来转载去的博客,内容都一模一样,DBA老师也不写博客,所以我就只能按我上述这个思路理解了,如果网友有什么问题欢迎指出~

一次MySQL死锁的排查记录的更多相关文章

  1. Mysql死锁如何排查:insert on duplicate死锁一次排查分析过程

    前言 遇到Mysql死锁问题,我们应该怎么排查分析呢?之前线上出现一个insert on duplicate死锁问题,本文将基于这个死锁问题,分享排查分析过程,希望对大家有帮助. 死锁案发还原 表结构 ...

  2. MySQL死锁系列-线上死锁问题排查思路

    前言 MySQL 死锁异常是我们经常会遇到的线上异常类别,一旦线上业务日间复杂,各种业务操作之间往往会产生锁冲突,有些会导致死锁异常.这种死锁异常一般要在特定时间特定数据和特定业务操作才会复现,并且分 ...

  3. 记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

    昨晚我正在床上睡得着着的,突然来了一条短信. 啥,线上MySQL死锁了,我赶紧登录线上系统,查看业务日志. 能清楚看到是这条insert语句发生了死锁. MySQL如果检测到两个事务发生了死锁,会回滚 ...

  4. 【错误记录】flask mysql 死锁

    最近使用flask-sqlalchemy时,进行测试的时候发现日志中打印出了MySql死锁错误,查看Mysql日志发现是因为有俩条sql出现了死锁: Deadlock found when tryin ...

  5. MySQL 死锁问题分析

    转载: MySQL 死锁问题分析 线上某服务时不时报出如下异常(大约一天二十多次):"Deadlock found when trying to get lock;". Oh, M ...

  6. 一次MySQL死锁问题解决

    一次MySQL死锁问题解决 一.环境 CentOS, MySQL 5.6.21-70, JPA 问题场景:系统有定时批量更新数据状态操作,每次更新上千条记录,表中总记录数约为500W左右. 二.错误日 ...

  7. mysql-不恰当的update语句使用主键和索引导致mysql死锁

    背景知识:MySQL有三种锁的级别:页级.表级.行级. MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking):BDB存储引擎采用的是页面锁(page-level ...

  8. 手把手教你分析解决MySQL死锁问题

    在生产环境中如果出现MySQL死锁问题该如何排查和解决呢,本文将模拟真实死锁场景进行排查,最后总结下实际开发中如何尽量避免死锁发生. 一.准备好相关数据和环境 当前自己的数据版本是8.0.22 mys ...

  9. Mysql 高负载排查思路

    Mysql 高负载排查思路 发现问题 top命令 查看服务器负载,发现 mysql竟然百分之两百的cpu,引起Mysql 负载这么高的原因,估计是索引问题和某些变态SQL语句. 排查思路 1. 确定高 ...

随机推荐

  1. 排序-InsertionSort 插入排序

    插入排序 の implementation 插入排序就像打赌的时候,比如双扣.抽牌的时候,一次拿一张牌,这张牌和之前的牌一张张比较.选择把这张牌插入什么位置,排好顺序的位置后打牌更顺.要不然得一个一个 ...

  2. 基于spring@aspect注解的aop实现

    第一步:编写切面类 package com.dascom.hawk.app.web.tool; import org.aspectj.lang.JoinPoint; import org.aspect ...

  3. PyQt(Python+Qt)学习随笔:QScrollArea为什么不起作用未出现滚动条?

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 老猿在进行Scroll Area部件测试时,在下面的窗体中放置了一个Scroll Area部件,在部 ...

  4. PyQt(Python+Qt)学习随笔:QAbstractItemView的defaultDropAction属性

    老猿Python博文目录 老猿Python博客地址# 一.概述 defaultDropAction属性用于控制QAbstractItemView及其子类的实例视图中拖放时放下的默认操作.该属性的类型为 ...

  5. PyQt(Python+Qt)学习随笔:Qt Designer中QAbstractButton派生按钮部件的icon属性和iconSize属性

    icon属性 icon属性保存按钮上展示的图标,图标的缺省大小由图形界面的样式决定,但可以通过 iconSize 属性进行调整. 图标的几种子属性状态的含义与QWidget的windowIcon属性相 ...

  6. swpuCTF2019 web1 无列名注入

    上周参加的swpuctf比赛第一道web题做了好久,在最后一个小时用非预期的方法做出来了,看了官方题解之后记录一下wp里面的无列名注入. 关于无列名注入可以看一下这篇链接 https://www.ch ...

  7. 巨经典论文!推荐系统经典模型Wide & Deep

    今天我们剖析的也是推荐领域的经典论文,叫做Wide & Deep Learning for Recommender Systems.它发表于2016年,作者是Google App Store的 ...

  8. Scrum冲刺_Day04

    一.团队展示: 1.项目:light_note备忘录 2.队名:删库跑路队 3.团队成员 队员(不分先后) 项目角色 黄敦鸿 后端工程师.测试 黄华 后端工程师.测试 黄骏鹏 后端工程师.测试 黄源钦 ...

  9. NOI Online #2 提高组 游记

    没 NOI Online 1 挂的惨就来写游记吧,不知道为啥 NOI Online 1 民间数据测得 60 分的 T1 最后爆零了... 昏昏沉沉的醒来,吃了早饭,等到 \(8:30\) 进入比赛网页 ...

  10. P4085 [USACO17DEC]Haybale Feast

    我又开始水了,感觉又是一道虚假的蓝题 题意 非常好理解,自己看吧 题解 可以比较轻易的发现,如果对于一段满足和大于等于 \(m\) 的区间和其满足和大于等于 \(m\) 的子区间来说,选择子区间肯定是 ...