概述

前几天排查了一个死锁问题,最开始百思不得其解,因为发生死锁的两个事务是单语句事务,语句类型相同(where属性列相同,仅值不同),而且语句都走了相同的索引,但最终确实发生了死锁。通过定位排查发现,问题的源头就是index_merge,死锁的原因也很普通,两个事务加锁顺序不同,并存在相互等待的情况。因为这个案例比较特殊,所以在此分享给大家。

死锁信息

拿到死锁问题,首先需要查看几个基本信息,包括死锁等待关系,表结构定义等。

1.表结构定义

Create Table: CREATE TABLE `t_xxx_customer` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`partner_id` bigint(20) unsigned DEFAULT NULL,
`customer_id` bigint(20) unsigned DEFAULT NULL,
`deleted` tinyint(4) DEFAULT NULL,
`partner_user_id` bigint(20) unsigned DEFAULT NULL,
`xxx_id` varchar(128) DEFAULT NULL,
`xxx_name` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `partner_id` (`partner_id`),
KEY `customer_id` (`customer_id`),
KEY `partner_user_id` (`partner_user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=140249 DEFAULT CHARSET=utf8;  

2.死锁信息提取与分析

通过show engine innodb status;命令可以获取innodb引擎中最近一次发生死锁的信息,信息如下

*** (1) TRANSACTION: UPDATE t_xxx_customer SET xxx_id='101', xxx_name='bbb' where customer_id=235646 and partner_id=1688 and deleted=0;

*** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 1640 page no 3947 n bits 432 index partner_id of table xxx.t_xxx_customer trx id 2625291980 lock_mode X locks rec but not gap Record lock, heap no 334 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 8; hex 0000000000000698; asc ;; 1: len 8; hex 0000000000021747; asc G;;

*** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 1640 page no 3395 n bits 160 index  PRIMARY of table t_xxx_customer trx id 2625291980 lock_mode X locks rec but not gap waiting Record lock, heap no 89 PHYSICAL RECORD: n_fields 25; compact format; info bits 0

*** (2) TRANSACTION: UPDATE t_xxx_customer SET xxx_id='102', xxx_name='aaa' where customer_id=151069 and partner_id=1688 and deleted=0;

*** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 1640 page no 3395 n bits 160 index PRIMARY of table xxx.t_xxx_customer trx id 2625291981 lock_mode X locks rec but not gap

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 1640 page no 3947 n bits 432 index partner_id of table xxx.t_xxx_customer trx id 2625291981 lock_mode X locks rec but not gap waiting Record lock, heap no 334 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 8; hex 0000000000000698; asc ;; 1: len 8; hex 0000000000021747; asc G;;

*** WE ROLL BACK TRANSACTION (2)

从死锁结果来看,我们很容易看到事务1持有 partner_id二级索引上的锁,等待PK索引上的锁;而事务2持有PK索引锁,等待partner_id二级索引上的锁,两个事务相互持有对方需要的锁资源,而无法往前推进,造成死锁。单从死锁信息来看,我们可能会比较疑惑,每个事务只有一个语句,为什么同样的语句,对二级索引和主键的加锁顺序会不同?

产生死锁的原因

首先我们来看看语句的执行计划,

语句的type是index_merge,Extra的信息是Using intersect(customerid,partnerid),从而我们得知语句执行计划走了index_merge优化,单个语句通过两个索引(customerid,partnerid)来提取记录集合并取交集获得最终结果集。index_merge具体算法不在此展开,基本使用场景是语句包含多个查询条件,每个条件都单独存在索引,而单个条件的索引过滤度不高,组合起来过滤度比较高,这个时候就可能会走index_merge优化,使得单个SQL语句可以同时利用两个索引过滤。会不会与index_merge有关呢?

在index_merge的情况下,会导致二级索引与主键索引顺序不一致的情况吗?结合上面的死锁信息,我们得知死锁两个的二级索引key是0x698,而主键索引key是0x21747。我们看看到底是哪条记录的主键和二级索引发生了死锁,

可以看到0x21747对应的customer_id为151069,partner_id为1688,是不是感觉似曾相识,对的,第二个事务的语句查询条件就是这两个条件的组合。这说明,对于这条记录,第一个事务语句只有partnerid索引(1688)满足条件;对于第二个事务,customer_id和partner_id索引都满足条件。由于每个语句执行时都需要利用两个二级索引,假设先使用customer_id索引扫描,然后使用partner_id索引扫描,那么对于id为0x21747的记录,事务1的partner_id=1688满足条件,加partner_id锁,然后对对应的PK索引加锁;对于事务2,对customer_id= 151069加锁,对对应的PK索引加锁,然后对partner_id=1688索引加锁。那么对partner_id二级索引和PK主键索引在两个事务的上锁顺序是相反的,所以导致了死锁。对于id为0x21747记录:

序号 事务1 事务2
1 customer_id 不满足条件不加锁 customer_id= 151069 加锁
2 partner_id=1688加锁 PK=0x21747加锁
3 PK=0x21747加锁 partner_id=1688加锁
4   PK=0x21747加锁

表格第2步和第3步,两个事务的加锁顺序是相反的,导致了死锁发生。

如何避免死锁

前面啰啰嗦嗦讲了一个死锁案例的来龙去脉,但仅仅是产生死锁的一种情况。生产环境中,我们当然不需要死锁频繁发生,毕竟是需要部分事务回滚才能解锁的,下面介绍几个简单的原则,有助于减少死锁的发生。

1)  尽量按顺序加锁,从源头避免死锁
2)选择合适的隔离级别,隔离级别越高,并发冲突越激烈,实际场景Read-Committed就够用了
3)避免使用大事务,根据二段锁原则,只有事务结束(提交或回滚)才会释放锁,持有的锁越多,可能导致的冲突越大
4)为表添加合适的索引,避免走不到索引导致对每条记录都加锁

index_merge引发的死锁排查的更多相关文章

  1. Java死锁排查和Java CPU 100% 排查的步骤整理

    ================================================= 人工智能教程.零基础!通俗易懂!风趣幽默!大家可以看看是否对自己有帮助! 点击查看高清无码教程 == ...

  2. SQL Server 隐式转换引发的死锁

    在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...

  3. 记录一次Mysql死锁排查过程

    背景 以前接触到的数据库死锁,都是批量更新时加锁顺序不一致而导致的死锁,但是上周却遇到了一个很难理解的死锁.借着这个机会又重新学习了一下mysql的死锁知识以及常见的死锁场景.在多方调研以及和同事们的 ...

  4. SQL Server死锁排查

    1. 死锁原理 根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态. 死锁的四个必要条件:互斥条件(Mutua ...

  5. 死锁排查的小窍门 --使用jdk自带管理工具jstack

    本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 开发时间久了,难免会写出一些一些死锁的代码,自己明明调用该方法可该方法就是不执行.不进该方法.日志也不打 ...

  6. SQL Server死锁排查经历 -基于SqlProfiler

     提到sql server,想必最让人头疼的当属锁机制了.在默认的read committed隔离模式下,连最基本的select操作都要申请各种粒度的锁,而且在读取数据过程中会不断有锁升级.转化.在非 ...

  7. 记一次Windb死锁排查

    正在开会,突然线上站点线程数破千.然后一群人现场dump分析. 先看一眼线程运行状态 !eeversion 发现CPU占用并不高,19%,937条线程正在运行. 看看他们都在干什么. ~* e !cl ...

  8. [转]Java死锁排查

    文章来源:微信公众号:猿天地 1. 死锁的概念: 是Java多线程情况下,两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,它们都讲无法推进下去.此时称 ...

  9. wait 和async,await一起使用引发的死锁问题

    在某个项目开发过程中,偶然间发现在UI线程中async,await,wait三者一起使用会引发一个必然性的死锁问题. 一个简单的实例,代码很简单,在界面上放置一个Button,并在Button的cli ...

随机推荐

  1. 字符串处理,NSNumber转换

    more:http://www.superqq.com/blog/categories/ioskai-fa/ 1.判断字符串是否为空 if ([text lenght] == 0) {     //  ...

  2. mysql,mybatis使用中遇到的类型转化的问题

    产生原因还没有明白,先记录一下. 使用DATEDIFF函数,计算两个日期的时间差.在mybatis中,resultType 是map.在代码中,根据map的key取值的时候. 在mysql 5.5.3 ...

  3. bzoj2152

    题解: 随便点分治,用一个t数组,t[i]代表有u到root的值mod3==i: 那么答案就是:t[0]*t[0]+t[1]*t[2]*2; 代码: #include<iostream> ...

  4. iOS 添加导航栏两侧按钮

    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"首页" style ...

  5. input框的默认bug解决办法

    input框的默认bug是在没干掉边框的情况下是不能设置背景颜色的,否则边框会变成内边框(黑色)效果,很难看. 解决办法是: none掉input框的边框:border:none; 再设置其背景色为任 ...

  6. MySQL5.6多实例部署

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://suifu.blog.51cto.com/9167728/1850560 无论是迫 ...

  7. CentOS 6.6下JDK1.7安装与配置(Linux)经典入门详解案例

    最近用的linux较多,在网站找了一些关于linux环境下jdk安装的教程,过程是有的但是好多细节都没有表现出来,所以我花了点时间总结了一下,希望对大家都有帮助... CentOS下JDK1.7安装与 ...

  8. js与php传递参数

    这个问题在网页开发时经常遇到,其实解决办法非常简单,就是几行代码的事,不过各种js.php书上都没有,百度下来也乱七八糟的,有的能用,有的不能用.小编遇到这问题时认认真真研究了一上午,研究出一点心得, ...

  9. Delphi隐藏进程

    interface function MyHideProcess: Boolean; implementation uses Windows, Classes, AclAPI, accCtrl; ty ...

  10. C语言的位运算的优势

    位运算加速技巧1. 如果乘上一个2的倍数数值,可以改用左移运算(Left Shift) 加速 300% x = x * 2;x = x * 64;//改为:x = x << 1; // 2 ...