一个锁的奇怪问题分析总结

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 一个锁的奇怪问题分析总结 问题的发生  下午收到一个业务同学反馈的yc_gd11x5_project表出现死锁的问题,现象是update的时候经常卡住很长时间,程序侧的日志如下:  UPDATE yc_gd11x5_project SET chupiao_state=...

一个锁的奇怪问题分析总结
问题的发生
 下午收到一个业务同学反馈的yc_gd11x5_project表出现死锁的问题,现象是update的时候经常卡住很长时间,程序侧的日志如下:
 UPDATE yc_gd11x5_project SET chupiao_state='1', chupiao_time='1404261677',chupiao_uid=51,real_chupiao_money=project_price WHERE project_no in ("GDXR1OHC1BF266","GDXR1OHC12CEC3","GDXR1OHC182135","GDXR2OHC19E6F0","GDXR2OHC1273D3","GDXR2OHC157492","GDXR2OHC1510D0","GDXR2OHC1A8E7B","GDXR2OHC177716","GDXR2OHC12CEDE","GDXR2OHC11C818","GDXR2OHC1D0A4B","GDXR2OHC137173","GDXR3OHC1FBE5B","GDXR3OHC127BF9","GDXR3OHC1IB1EW","GDXR3OHC1EF0C3","GDXR3OHC10E9B4","GDXR3OHC1F5A07","GDXR3OHC1O4MNO","GDXR3OHC17SOHV","GDXR3OHC13717B","GDXR3OHC1092E7","GDXR3OHC135805","GDXR3OHC1371D8","GDXR3OHC106E07","GDXR3OHC1755E4","GDXR3OHC1A7D76","GDXR3OHC1FD9B9","GDXR3OHC15FC9E","GDXR3OHC17F5D3","GDXR3OHC1A766F","GDXR3OHC1A38C0","GDXR3OHC1886AC","GDXR3OHC1D3E61","GDXR3OHC127B6A","GDXR3OHC1FDDFD","GDXR3OHC10E256","GDXR3OHC14D2BC","GDXR4OHC109D1C","GDXR4OHC157C92","GDXR4OHC1AF2BE","GDXR4OHC1C73BB","GDXR4OHC1F3203","GDXR4OHC1CEF1E","GDXR4OHC13821E","GDXR4OHC135DDD","GDXR4OHC1A87E6","GDXR4OHC1E472C","GDXR5OHC154C24","GDXR5OHC10253D","GDXR5OHC16D315","GDXR5OHC19E539","GDXR5OHC1ZQQC8","GDXR5OHC18CDF4","GDXR5OHC13B589","GDXR5OHC13DE3F","GDXR5OHC19C295","GDXR5OHC176139","GDXR5OHC14C078","GDXR5OHC1FD7F5","GDXR5OHC1F9DB6","GDXR5OHC11A97E","GDXR5OHC1825E3","GDXR5OHC19EA99","GDXR5OHC1B8707","GDXR5OHC1U3EB6","GDXR5OHC11AB20","GDXR5OHC1F901A","GDXR5OHC1C54BB","GDXR5OHC127366","GDXR5OHC157A55","GDXR5OHC1B5793","GDXR5OHC1F587C","GDXR5OHC102A3C","GDXR5OHC1CE054","GDXR5OHC19C2BE","GDXR5OHC1770D5","GDXR5OHC191179","GDXR5OHC177358","GDXR5OHC136E24","GDXR5OHC1CE28D","GDXR5OHC1FA236","GDXR5OHC121D60","GDXR5OHC19B2B1","GDXR5OHC1370A1","GDXR5OHC1AB988","GDXR5OHC186305","GDXR5OHC1AF6E3","GDXR5OHC14NRTC","GDXR5OHC1E4DB0","GDXR5OHC1E99E4","GDXR5OHC13B7FC","GDXR5OHC121BBA","GDXR5OHC1C7F5D","GDXR5OHC1B5A55","GDXR5OHC127653","GDXR5OHC1B5E4A","GDXR5OHC18CF7D","GDXR5OHC1VLQ6A") and chupiao_state=0 and shenhe_state!=-1, msg:Lock wait timeout exceeded; try restarting transaction]/usermsg[pid:, SetBatchChupiaoState error]
在开始分析之前先介绍一下yc_gd11x5_project表的情况,具体情况如下图所示:
表名 数据量 字段数 唯一索引
yc_gd11x5_project 12863104 85 project_no


问题分析
问题分析之初步接触
第一反应应该是死锁导致,先用show full processlist去找到正在运行的线程,没有抓取到对一个堵住的SQL,可能是达到死锁的超时时间自动对小的事务做了回滚。使用show engine innodb status\G;查看innodb中锁的信息,发现如下线索

 

确实有死锁,不过时间不对,从innodb日志看锁是在14:16的时候发生的,业务开发的同学反馈在16点的时候也有发生过update超时的情况。如果16点有发生过那么show engine innodb status\G; 看到的最近的一次死锁就不应该还是14:16分。因此初步判断业务开发同学反馈的update超时不应该是死锁,而是update在等待某个锁的时候执行超时了。既然是update在等待锁,那么就肯定有某个线程在持有锁,而且很长时间不释放。下面弄个抓取脚本就来个“现场”体验吧。
第一次亲临“现场”:
为了防止innodb的锁情况被冲掉,先将show engine innodb status\G;记录到mysql的错误日志中,要激活这个方式只需要创建一张名字为innodb_monitor的表,代码如下:
CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;
如果要取消这种方式,直接drop掉这个表即可。
写了个简单的抓取脚本,如果出现update超过8秒的时候就记录,就抓取show engine innodb status\G和show full processlist的信息。根据以往的经验有了这两个如果是死锁或者锁等待的情况,基本都能定位到对应的问题。 过了20分钟左右又出现超时了,此时看peocesslist的信息update都处于updating状态。

很奇怪,这种根据唯一索引做update的不可能要update那么久,难道是对应的update语句在事务中没有提交?电话业务的开发同事进行确认,业务开发的同学答复那些SQL都是自动提交的,没有放在显式的事务中。好奇怪,那么是什么导致SQL执行那么长的时间呢?初步判断这个update最大的可能是在等待获取锁。那么是什么语句在持有锁呢?如何才能找到真正持有锁的线程呢?记得之前阅读《高性能MYSQL》里有提到mysqladmin开启debug模式能查看到具体哪个线程在持有锁。
第二次亲临“现场"
于是改进脚本,达到触发条件的时候开启debug,再继续等待,晚上9点半的时候终于又重现了,这次看看战果如何?
Processlist的信息有了,抓到两个执行10秒的SQL。

下面是debug模式找到的线程锁的情况:

诡异,这次debug显示是update的语句在持有锁,这个怎么解释都解释不通,怀疑这个debug有bug。
第三次亲临“现场”
继续尝试,记得之前看MYSQL 5.5的文档的时候有提到说信息库中能记录关于线程持有锁、锁等待、以及事务锁等信息,于是查看官方文档,发现了有三个很有用的表,分别介绍一下:
information_schema.INNODB_TRX
这个表包含了每个事务在innodb内部的当前状态,包括当事务开始的时候或者事务在执行中是否在等待锁。
information_schema.INNODB_LOCK_WAITS
顾名思义,这个表显示那个事务在等待锁。
information_schema.INNODB_LOCKS
这个表记录了innodb的所有锁的情况,这里可以看到锁的信息、SQL线程ID以及锁定的数据等东东。
有了这些法宝再去更改抓取程序,当出现问题的时候抓取这三个表的状态。上午10点的多的时候又重现了,这次清晰了,先截图来分析一下:
INNODB_LOCKS对应的信息如下:

INNODB_LOCK_WAITS对应的信息如下:

INNODB_TRX对应的信息如下:

有了这些信息足以了,综合上面3个表的信息可以知道,那个update语句是在等待锁,那个线程ID为718528318的线程才是真正持有锁的语句。通过抓取的线程过滤一下看看:

从这里可以断定是某个事物中执行了查询表的操作后没有提交事务导致,将10.205.138.165提供给开发的同学检查后反馈确实有一个地方存在问题,截图如下:

这里如果insert失败后就直接return了,由于是长连接,因此这个事务没有提交。再看失败的日志发现有duplicate key导致insert失败的情况造成事务没有提交。再结合INNODB_LOCKS进行分析可知,insert project_no为GDXR3OHD70840D的时候由于duplicate key冲突了,导致insert失败,但是对GDXR3OHD70840D的锁还没有释放,因此在对这个project_no更新的时候就一直在等待。

找到问题就好解决了,如果insert失败就执行rollback的操作就OK了。自此世界安静了。
这里为什么是共享锁的,应该是插入的时候根据唯一索引,还要查询一下数据是否存在,因此加了共享锁。
问题重现
通过上面的分析,模拟出分析的过程应该就能重现该问题,为了验证推论的结果是否正确,因此手工来重现一把。先创建一个表,并插入几条记录:


开启两个终端然后分别模拟线上出现的场景:
A终端模拟线上insert失败的操作


B终端模拟update的操作:


和线上出现的问题一样,当A终端进行commit或者rollback或者连接中断后B终端的update才能获取锁。和现场环境一样。

问题疑问
那三个lock的表只有5.5版本或者innodb plugin的版本才有。如果是5.1的没有安装innodb plugin的mysql该如何去找到持有锁的线程呢?等有时间了再来好好研究一下!

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6月前
|
Java
Java并发编程中的死锁问题及解决方法
【2月更文挑战第11天】 在Java并发编程中,死锁是一个常见但又非常棘手的问题。本文将深入探讨死锁的概念、产生原因以及常见的解决方法,帮助读者更好地理解并发编程中的挑战,并提供实用的解决方案。
92 6
|
5月前
|
Python
Python多线程中递归锁如何解决死锁问题的详细阐述
Python多线程中递归锁如何解决死锁问题的详细阐述
|
6月前
|
算法
出现线程死锁缺陷一般有那些原因?该怎么解决?
出现线程死锁缺陷一般有那些原因?该怎么解决?
76 1
|
安全
什么是死锁?(把死锁给大家讲明白,知道是什么,为什么用,怎么用)
什么是死锁?(把死锁给大家讲明白,知道是什么,为什么用,怎么用)
90 0
什么是死锁?(把死锁给大家讲明白,知道是什么,为什么用,怎么用)
|
Java 数据库连接 数据库
这些让Spring事务失效的操作千万别犯!
在Spring框架中,事务是一种关键机制,用于确保数据库操作的一致性和完整性。然而,有时候事务可能会失效,导致意外的结果或数据不一致。下面是一些可能导致Spring事务失效的常见场景,以及相应的代码示例和解释。
这些让Spring事务失效的操作千万别犯!
|
存储 安全 Java
小白也能看懂的锁升级过程和锁状态
小白也能看懂的锁升级过程和锁状态
260 0
小白也能看懂的锁升级过程和锁状态
|
存储 安全 Java
看完你就明白的锁系列之锁的状态
前面两篇文章我介绍了一下 看完你就应该能明白的悲观锁和乐观锁 看完你就明白的锁系列之自旋锁 看完你就会知道,线程如果锁住了某个资源,致使其他线程无法访问的这种锁被称为悲观锁,相反,线程不锁住资源的锁被称为乐观锁,而自旋锁是基于 CAS 机制实现的,CAS又是乐观锁的一种实现,那么对于锁来说,多个线程同步访问某个资源的流程细节是否一样呢?换句话说,在多线程同步访问某个资源时,锁的状态会如何变化呢?本篇文章来探讨一下。
99 0
看完你就明白的锁系列之锁的状态
|
NoSQL Java Linux
咋办,死锁了
死锁的概念; 模拟死锁问题的产生; 利用工具排查死锁问题; 避免死锁问题的发生;
咋办,死锁了
|
算法 Java Linux
如果面试官让你分析类初始化阶段的死锁现象
哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。
95 0
如果面试官让你分析类初始化阶段的死锁现象
J3
|
存储 安全 Java
synchronized解析及锁膨胀过程,面试再也不怕了
synchronized解析及锁膨胀过程,面试再也不怕了
J3
543 0
synchronized解析及锁膨胀过程,面试再也不怕了