Mysql MDL+DDL 死锁

应用背景:

应用A,springboot2+shardingJdbc5.1架构, 使用mysql数据库,其中有些订单表为分区表,按日分区。

问题描述

今日在生产环境维护分区表分区时,应用报了死锁。具体日志如下

image.png

找到DBA去排查时,基于他们的经验,他们不承认这是数据库报出的死锁,反而怀疑是我们的应用代码或者JDBC有问题,这 ...

问题复现

在排查相关资料后,我断定了是mysql的锁的机制的问题,于是我做了个测试

应用: session1   客户端: session2

session1: 开启事务,并执行查询,持有 XXX 对象的 SHARED_READ 锁,简称SR锁。
session2: 执行添加分区(DDL)命令,想要获取 XXX 对象的 EXCLUSIVE 锁,简称X锁.


这个状态时,session2 在等 session1 释放锁。
 
sessin1: 继续执行 update 命令,会申请 XXX 对象的 SHARED_WRITE 锁,简称SW锁。
这个状态时,session2 在等待获取 XXX 对象的X锁,session1 想要申请SW锁,必须等session2 释放掉锁。
所以 session1 在等 session2 释放锁。
 
两个会话互相等待,发生死锁,MySQL数据库会自动回滚其中一个事务。

session1 :
企业微信截图_16724710876448.png

session2:
企业微信截图_16724711031026.png

此刻死锁已经出现,我们来看下数据库里的死锁日志

企业微信截图_16724711911842.png

测试的结果和我预想or我们遇到的一样,是X锁和SW锁的相互互斥的机制导致的死锁,而这种死锁不会在mysql的死锁日志中记录

问题原因

先给大家看一下mysql的锁的兼容矩阵

Request   |  Granted requests for lock                  |
 type     | S  SH  SR  SW  SWLP  SU  SRO  SNW  SNRW  X  |
----------+---------------------------------------------+
S         | +   +   +   +    +    +   +    +    +    -  |
SH        | +   +   +   +    +    +   +    +    +    -  |
SR        | +   +   +   +    +    +   +    +    -    -  |
SW        | +   +   +   +    +    +   -    -    -    -  |
SWLP      | +   +   +   +    +    +   -    -    -    -  |
SU        | +   +   +   +    +    -   +    -    -    -  |
SRO       | +   +   +   -    -    +   +    +    -    -  |
SNW       | +   +   +   -    -    -   +    -    -    -  |
SNRW      | +   +   -   -    -    -   -    -    -    -  |
X         | -   -   -   -    -    -   -    -    -    -  |
根据mysql锁兼容矩阵图可以看出,X锁和任何锁是不兼容的(敲黑板,背下来).

我们的测试场景下,session2 在等待获取 xxx 对象的X锁,session1 想要申请SW锁,必须等session2 释放掉锁。
所以 session1 在等 session2 释放锁 , 进而产生死锁

解决方案

1、在session1中的查询,加上 for update, 使得session1 一开始就获取SW锁
2、将session1的查询独立出当前事务
3、优化 mysql, 将DDL操作改写成软提交方式, 获取不到锁后,释放已经拿到的锁,然后不断重试

写在后面

如何安全地给表执行DDL操作(参考 http://nwjs.net/news/249294.html

1、生产环境的任何大表或频繁操作的小表,ddl都要非常慎重,最好在业务低峰期执行。
2、设计上要尽可能避免大事务,大事务不仅仅会带来各种锁问题,还会引起复制延迟/回滚空间爆满等各类问题。
3、设置参数 lock_wait_timeout 为较小值,使被阻塞端主动停止。
4、增强监控告警,及时发现 MDL 锁。
5、或许这样操作也是一种好办法:按新结构创建新表 -> 将旧表数据迁移至新表 -> 重命名两个表(三步都通过编写sql语句完成,比手动操作快,第二步的数据迁移操作视情况而定)。过程中最好在没人用的时候操作
6、操作ddl之前,先用以下语句查一下有没有长事务:
SELECT * FROM information_schema.INNODB_TRX;
7、多副本(主从、集群)下可以做热更新。

以上,仅代表个人观点,大家有方案可以评论区交流,欢迎大家批评指正

人行天地间 忽如远行客

27 声望
7 粉丝
0 条评论
推荐阅读
花了几个月时间把 MySQL 重新巩固了一遍,梳理了一篇几万字 “超硬核” 的保姆式学习教程!(持续更新中~)
MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。

民工哥14阅读 2k

封面图
终于卷完了!Redis 打怪升级进阶成神之路(2023 最新版)!
是一种非关系型数据库服务,它能解决常规数据库的并发能力,比如传统的数据库的IO与性能的瓶颈,同样它是关系型数据库的一个补充,有着比较好的高效率与高性能。专注于key-value查询的redis、memcached、ttserver。

民工哥11阅读 989

封面图
硬卷完了!MongoDB 打怪升级进阶成神之路( 2023 最新版 )!
前面我们学习:MySQL 打怪升级进阶成神之路、Redis 打怪升级进阶成神之路,然后我们还在继续 NoSQL 的卷王之路。从第一篇文章开始,我们逐步详细介绍了 MogoDB 基础概念、安装和最基本的CURD操作、索引和聚合、工...

民工哥6阅读 553

封面图
初学后端,如何做好表结构设计?
这篇文章介绍了设计数据库表结构应该考虑的4个方面,还有优雅设计的6个原则,举了一个例子分享了我的设计思路,为了提高性能我们也要从多方面考虑缓存问题。

王中阳Go4阅读 1.7k评论 2

封面图
又一款内存数据库横空出世,比 Redis 更强,性能直接飙升一倍!杀疯了
KeyDB是Redis的高性能分支,专注于多线程,内存效率和高吞吐量。除了多线程之外,KeyDB还具有仅在Redis Enterprise中可用的功能,例如Active Replication,FLASH存储支持以及一些根本不可用的功能,例如直接备份...

民工哥4阅读 1.7k评论 2

封面图
面试官:请说一下如何优化结构体的性能?
使用内存对齐机制优化结构体性能,妙啊!前言之前分享过2篇结构体文章:10秒改struct性能直接提升15%,产品姐姐都夸我好棒 和 Go语言空结构体这3种妙用,你知道吗? 得到了大家的好评。这篇继续分享进阶内容:结...

王中阳Go4阅读 3.8k评论 2

封面图
Vue+Express+Mysql全栈项目之增删改查、分页排序导出表格功能
本文记录一下实现一个全栈项目,前端使用vue框架、后端使用express框架、数据库使用mysql。此项目的意义不仅仅有助于我们复习nodejs相关知识、更有助于带前端新人,使其快速从整体全局角度中,理解常规后台管理系...

水冗水孚4阅读 2.6k

人行天地间 忽如远行客

27 声望
7 粉丝
宣传栏