返回顶部
首页 > 资讯 > 数据库 >记一次神奇的Mysql死锁排查
  • 198
分享到

记一次神奇的Mysql死锁排查

2024-04-02 19:04:59 198人浏览 独家记忆
摘要

背景说起Mysql死锁,之前写过一次有关mysql加锁的基本介绍,对于一些基本的Mysql锁或者死锁都有一个简单的认识,可以看下这篇文章为什么开发人员需要了解分布式锁。有了上面的经验之后,本以为对于死锁都能

背景

说起Mysql,之前写过一次有关mysql加锁的基本介绍,对于一些基本的Mysql锁或者死锁都有一个简单的认识,可以看下这篇文章为什么开发人员需要了解分布式锁。有了上面的经验之后,本以为对于死锁都能手到擒来,没想到再一个阳光明媚的下午报出了一个死锁,但是这一次却没想象的那么简单。

问题初现

在某天下午,突然系统报警,抛出个异常:

记一次神奇的Mysql死锁排查

仔细一看好像是事务回滚异常,写着的是因为死锁回滚,原来是个死锁问题,由于我对Mysql锁还是有一定
了解的,于是开始主动排查这个问题。

首先在数据库中查找Innodb Status,在Innodb Status中会记录上一次死锁的信息,输入下面命令:

SHOW ENGINE INNODB STATUS

死锁信息如下,sql信息进行了简单处理:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-02-22 15:10:56 0x7eec2f468700
*** (1) TRANSACTioN:
TRANSACTION 2660206487, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WaiT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 31261312, OS thread handle 139554322093824, query id 11624975750 10.23.134.92 erp_crm__6f73 updating
UPDATE tenant_config SET
       open_card_point =  0
       where tenant_id = 123
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1322 page no 534 n bits 960 index uidx_tenant of table `erp_crm_member_plan`.`tenant_config` trx id 2660206487 lock_mode X locks rec but not gap waiting

*** (2) TRANSACTION:
TRANSACTION 2660206486, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 31261311, OS thread handle 139552870532864, query id 11624975758 10.23.134.92 erp_crm__6f73 updating
UPDATE tenant_config SET
       open_card_point =  0
       where tenant_id = 123
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1322 page no 534 n bits 960 index uidx_tenant of table `erp_crm_member_plan`.`tenant_config` trx id 2660206486 lock mode S
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1322 page no 534 n bits 960 index uidx_tenant of table `erp_crm_member_plan`.`tenant_config` trx id 2660206486 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (1)
------------

给大家简单的分析解释一下这段死锁日志,事务1执行Update语句的时候需要获取uidx_tenant这个索引再where条件上的X锁(行锁),事务2执行同样的Update语句,也在uidx_tenant上面想要获取X锁(行锁),然后就出现了死锁,回滚了事务1。当时我就很懵逼,回想了一下死锁产生的必要条件:

  1. 互斥。

  2. 请求与保持条件。

  3. 不剥夺条件。

  4. 循环等待。
    从日志上来看事务1和事务2都是取争夺同一行的行锁,和以往的互相循环争夺锁有点不同,怎么看都无法满足循环等待条件。经过同事提醒,既然从死锁日志中不能进行排查,那么就只能从业务代码和业务日志从排查。这段代码的逻辑如下:

public int saveTenantConfig(PoiContext poiContext, TenantConfigDO tenantConfig) {
        try {
            return tenantConfigMapper.saveTenantConfig(poiContext.getTenantId(), poiContext.getPoiId(), tenantConfig);
        } catch (DuplicateKeyException e) {
            LOGGER.warn("[saveTenantConfig] 主键冲突,更新该记录。context:{}, config:{}", poiContext, tenantConfig);
            return tenantConfigMapper.updateTenantConfig(poiContext.getTenantId(), tenantConfig);
        }
    }

这段代码的意思是保存一个配置文件,如果发生了唯一索引冲突那么就会进行更新,当然这里可能写得不是很规范,其实可以用

insert into ... 
on duplicate key update 

也可以达到同样的效果,但是就算用这个其实也会发生死锁。看了代码之后同事又给我发了当时业务日志,

记一次神奇的Mysql死锁排查

可以看见这里有三条同时发生的日志,说明都发生了唯一索引冲突进入了更新的语句,然后发生的死锁。到这里答案终于稍微有点眉目了。

这个时候再看我们的表结构如下(做了简化处理):

CREATE TABLE `tenant_config` (
  `id` bigint(21) NOT NULL AUTO_INCREMENT,
  `tenant_id` int(11) NOT NULL,
  `open_card_point` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_tenant` (`tenant_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT

我们的tenant_id是用来做唯一索引,我们的插入和更新的where条件都是基于唯一索引来操作的。

UPDATE tenant_config SET
       open_card_point =  0
       where tenant_id = 123

到了这里感觉插入的时候对唯一索引加锁有关系,接下来我们进行下一步的深入剖析。

深入剖析

上面我们说有三个事务进入update语句,为了简化说明这里我们只需要两个事务同时进入update语句即可,下面的表格展示了我们整个的发生过程:

记一次神奇的Mysql死锁排查

小提示:S锁是共享锁,X锁是互斥锁。一般来说X锁和S,X锁都互斥,S锁和S锁不互斥。

我们从上面的流程中看见发生这个死锁的关键需要获取S锁,为什么我们再插入的时候需要获取S锁呢?因为我们需要检测唯一索引?在RR隔离级别下如果要读取那么就是当前读,那么其实就需要加上S锁。这里发现唯一键已经存在,这个时候执行update就会被两个事务的S锁互相阻塞,从而形成上面的循环等待条件。

记一次神奇的Mysql死锁排查

小提示: 在mvcC中,当前读和快照读的区别:当前读每次需要加锁(可以使共享锁或者互斥锁)获取到最新的数据,而快照读是读取的是这个事务开始的时候那个快照,这个是通过undo log去进行实现的。

这个就是整个死锁的原因,能出现这种死锁的还有一个情况,就是同一时间来三个插入操作,其中先插入的那个事务如果最后回滚了,其余两个事务也会出现这种死锁。

解决方案

这里的核心问题是需要把S锁给干掉,这里有三个可供参考的解决方案:

  • 将RR隔离级别,降低成RC隔离级别。这里RC隔离级别会用快照读,从而不会加S锁。

  • 再插入的时候使用select * for update,加X锁,从而不会加S锁。

  • 可以提前加上分布式锁,可以利用Redis,或者ZK等等,分布式锁可以参考我的这篇文章。聊聊分布式锁

第一种方法不太现实,毕竟隔离级别不能轻易的修改。第三种方法又比较麻烦。所以第二种方法是我们最后确定的。

总结

说了这么多,最后做一个小小的总结吧。排查死锁这种问题的时候有时候光看死锁日志有时候会解决不了问题,需要结合整个的业务日志,代码以及表结构来进行分析,才能得到正确的结果。当然上面有一些数据库锁的基本知识如果不了解可以查看我的另一篇文章为什么开发人员需要了解分布式锁。

最后这篇文章被我收录于JGrowing-CaseStudy篇,一个全面,优秀,由社区一起共建的Java学习路线,如果您想参与开源项目的维护,可以一起共建,GitHub地址为:https://github.com/javagrowing/JGrowing

您可能感兴趣的文档:

--结束END--

本文标题: 记一次神奇的Mysql死锁排查

本文链接: https://lsjlt.com/news/45298.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 记一次神奇的Mysql死锁排查
    背景说起Mysql死锁,之前写过一次有关Mysql加锁的基本介绍,对于一些基本的Mysql锁或者死锁都有一个简单的认识,可以看下这篇文章为什么开发人员需要了解分布式锁。有了上面的经验之后,本以为对于死锁都能...
    99+
    2024-04-02
  • 一次神奇的MySQL死锁排查记录
    背景 说起Mysql死锁,之前写过一次有关Mysql加锁的基本介绍,对于一些基本的Mysql锁或者死锁都有一个简单的认识,可以看下这篇文章为什么开发人员需要了解数据库锁。有了上面的经验之后,本以为对于死锁都...
    99+
    2024-04-02
  • 一次线上MySQL死锁告警原因排查
    项目场景:一次线上MySQL死锁告警原因排查 最近处理了一次线上数据告警,记录一下。 问题描述 同步书架书籍的接口频繁抛出异常,提示数据库出现死锁,异常如下: 本日异常次数:2,异常日志:java.lang.RuntimeExcept...
    99+
    2021-02-11
    一次线上MySQL死锁告警原因排查
  • MySQL中怎么排查死锁
    这篇文章将为大家详细讲解有关MySQL中怎么排查死锁,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。死锁起因先介绍一下数据库和表情况,因为涉及到公司内部真是的...
    99+
    2024-04-02
  • 如何排查MySQL死锁警告
    本篇内容介绍了“如何排查MySQL死锁警告”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 故障背...
    99+
    2024-04-02
  • Mysql死锁排查实例分析
    这篇文章主要介绍“Mysql死锁排查实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Mysql死锁排查实例分析”文章能帮助大家解决问题。   问题初现  ...
    99+
    2024-04-02
  • 记一次MySQL流量问题的排查之旅
    导读: 作者:知数堂学员-邓志航;MySQL DBA,天生的MySQL爱好者,热衷于为他人解决问题,善于总结和分享。对数据平台构建和排查疑难问题有非常浓厚的兴趣 一、简介 记一次mysql流量问题的排查之旅 二、问题描述 在每天的业务高...
    99+
    2016-07-10
    记一次MySQL流量问题的排查之旅
  • MySQL 死锁异常排查和处理
    场景 删除车辆信息同时异步通知查询服务更新 查询服务采用 insert into view 方式增加数据(导致行级锁) 查询服务和删除车辆 争夺车辆表的锁,造成死锁 服务报错日志 2022-...
    99+
    2023-09-28
    mysql java
  • mysql死锁怎么排查及解决
    MySQL死锁是指两个或多个事务互相持有对方需要的资源,同时又等待对方释放资源,导致系统无法继续进行下去的情况。解决MySQL死锁问...
    99+
    2023-09-21
    mysql
  • MySQL升级WRITE_SET后的一次死锁分析
    导致我们死锁的现象是: 我们发现开启了write_set并行回放的实例从库上死锁的概率比以前高了不少, 并且发生死锁的实例都是在进行xtrabackup备份。本文主要分析这些数据库实例上发生死锁的原因。 场景 我们知道MySQL事物会设计到...
    99+
    2021-06-23
    MySQL升级WRITE_SET后的一次死锁分析
  • 记一次mysql中文字符乱码的问题排查
    今天开发反应两样的程序往一个库里面插入数据正常,往另外一个库里面插入数据有乱码。第一反应就是两个数据库关于字符集的配置不一样。在两个库分别查看参数:show variables like "%char%";...
    99+
    2024-04-02
  • mysql死锁排查及解决的方法是什么
    MySQL死锁是指两个或多个事务相互等待对方持有的资源,导致无法继续执行的情况。为了排查和解决MySQL死锁,可以采取以下方法:1....
    99+
    2023-08-16
    mysql
  • 一文学习MySQL 意向共享锁、意向排他锁、死锁
    目录一、InnoDB表级锁二、意向共享锁和意向排他锁三、死锁1. 数据库中的死锁2. 死锁场景以及解决办法3. 操作四、锁的优化建议一、InnoDB表级锁 我们知道,InnoDB是支...
    99+
    2024-04-02
  • ORACLE死锁(ORA-00060)故障排查的一般性手法
    ORACLE死锁(ORA-00060)故障排查的一般性手法       【背景】 今晨巡检发现SAP PRD产生一个死锁: Sun Ju...
    99+
    2024-04-02
  • 一篇文章学会java死锁与CPU 100%的排查
    目录 00 本文简介01 Java死锁排查和解决1、啥是死锁?2、为啥子会出现死锁?3、怎么排查代码中出现了死锁?【重点来了】第一个姿势:使用 jps + jstack第二个姿势:使...
    99+
    2024-04-02
  • 一次现场mysql重复记录数据的排查处理实战记录
    目录前言 分析 数据总计 重复次数占比 where 和 having 的区别 总结 前言 我当时正好出差在客户现场部署调试软件,有一天客户突然找到我这里,说他们...
    99+
    2024-04-02
  • 一次NodeJS内存泄漏排查的实战记录
    目录前言案例一故障现象排查过程案例二故障现象排查过程问题原因node-v9.x 以下的版本node-v10.x 以上的版本修复泄露总结前言 性能问题(内存、CPU 飙升导致服务重启、...
    99+
    2024-04-02
  • MySQL的意向共享锁、意向排它锁和死锁
    目录一、InnoDB的表级锁二、意向共享锁和意向排它锁三、死锁1. 数据库中的死锁2. 死锁场景以及解决方法3. 操作三、锁的优化建议一、InnoDB的表级锁 在绝大多数情况下应该使用行锁,因为事务和行锁往往是选择Inn...
    99+
    2022-07-14
    MySQL意向共享锁 MySQL意向排它锁 MySQL死锁
  • 排查Mysql突然变慢的一次过程
    排查Mysql突然变慢的一次过程 本文源地址:排查Mysql突然变慢的一次过程 上周客户说系统突然变得很慢,而且时不时的蹦出一个 404 和 500,弄得真的是很没面子,而恰巧出问题的时候正在深圳出差,所以一直没有时间 看问题,一直...
    99+
    2015-04-20
    排查Mysql突然变慢的一次过程
  • sql server中死锁排查的示例分析
    这篇文章主要介绍sql server中死锁排查的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!死锁的四个必要条件:互斥条件(Mutual exclusion):资源不能被共享...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作