当前位置:

首页 > 编程开发 > MySQL优化:如何解决因index merge引起的死锁?

MySQL优化:如何解决因index merge引起的死锁?

本文目录

    背景生产环境出现死锁流水,通过查看死锁日志,看到造成死锁的是两条一样的update语句(只有where条件中的值不同),如下:UPDATEtest_tableSET`status`=1WHERE`trans_id`='xxx1'AND`status`=0;UPDATEtest_tableSET`status`=1WHERE`trans_id`='xxx2'AND`status`=0;一开始比较费解,通过大量查询跟学习后,分析出了死锁形成的具体原理,特分享给大家,希望能帮助到

    背景

    生产环境出现死锁流水,通过查看死锁日志,看到造成死锁的是两条一样的update语句(只有where条件中的值不同),

    如下:

    UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0;
    UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0;

    一开始比较费解,通过大量查询跟学习后,分析出了死锁形成的具体原理,特分享给大家,希望能帮助到遇到同样问题的朋友。

    因为MySQL知识点较多,这里对很多名词不进行过多介绍,有兴趣的朋友,可以后续进行专项深入学习。

    死锁日志

    *** (1) TRANSACTION:
    TRANSACTION 791913819, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999
    mysql tables in use 3, locked 3
    LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s)
    MySQL thread id 462005230, OS thread handle 0x7f55d5da3700, query id 2621313306 x.x.x.x test_user Searching rows for update
    UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0;
    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 110 page no 39167 n bits 1056 index `idx_status` of table `test`.`test_table` trx id 791913819 lock_mode X waiting
    Record lock, heap no 495 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
    
    *** (2) TRANSACTION:
    TRANSACTION 791913818, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999
    mysql tables in use 3, locked 3
    5 lock struct(s), heap size 1184, 4 row lock(s)
    MySQL thread id 462005231, OS thread handle 0x7f55cee63700, query id 2621313305 x.x.x.x test_user Searching rows for update
    UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0;
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 110 page no 39167 n bits 1056 index `idx_status` of table `test`.`test_table` trx id 791913818 lock_mode X
    Record lock, heap no 495 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
    
    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 110 page no 41569 n bits 88 index `PRIMARY` of table `test`.`test_table` trx id 791913818 lock_mode X locks rec but not gap waiting
    Record lock, heap no 14 PHYSICAL RECORD: n_fields 30; compact format; info bits 0
    
    *** WE ROLL BACK TRANSACTION (1)

    简要分析下上边的死锁日志:

    • 1、第一块内容(第1行到第9行)中,第6行为事务(1)执行的SQL语句,第7和第8行意思为事务(1)在等待 idx_status 索引上的X锁;

    • 2、第二块内容(第11行到第19行)中,第16行为事务(2)执行的SQL语句,第17和第18行意思为事务(2)持有 idx_status 索引上的X锁;

    • 3、第三块内容(第21行到第23行)的意思为,事务(2)在等待 PRIMARY 索引上的X锁。(but not gap指不是间隙锁)

    • 4、最后一句的意思即为,MySQL将事务(1)进行了回滚操作。

    表结构

    CREATE TABLE `test_table` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `trans_id` varchar(21) NOT NULL,
    `status` int(11) NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uniq_trans_id` (`trans_id`) USING BTREE,
    KEY `idx_status` (`status`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

    通过表结构可以看出,trans_id 列上有一个唯一索引uniq_trans_id ,status 列上有一个普通索引idx_status ,id列为主键索引 PRIMARY 。

    InnoDB引擎中有两种索引:

    • 聚簇索引: 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据。

    • 辅助索引: 辅助索引叶子节点存储的是主键值,也就是聚簇索引的键值。

    主键索引 PRIMARY 就是聚簇索引,叶子节点中会保存行数据。uniq_trans_id 索引和idx_status 索引为辅助索引,叶子节点中保存的是主键值,也就是id列值。

    当我们通过辅助索引查找行数据时,先通过辅助索引找到主键id,再通过主键索引进行二次查找(也叫回表),最终找到行数据。

    执行计划

    MySQL优化:如何解决因index merge引起的死锁?

    通过看执行计划,可以发现,update语句用到了索引合并,也就是这条语句既用到了 uniq_trans_id 索引,又用到了 idx_status 索引,Using intersect(uniq_trans_id,idx_status)的意思是通过两个索引获取交集。

    为什么会用 index_merge(索引合并)

    MySQL5.0之前,一个表一次只能使用一个索引,无法同时使用多个索引分别进行条件扫描。但是从5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引分别进行条件扫描。

    如执行计划中的语句:

    UPDATE test_table SET `status` = 1 WHERE `trans_id` = '38' AND `status` = 0 ;

    MySQL会根据 trans_id = ‘38’这个条件,利用 uniq_trans_id 索引找到叶子节点中保存的id值;同时会根据 status = 0这个条件,利用 idx_status 索引找到叶子节点中保存的id值;然后将找到的两组id值取交集,最终通过交集后的id回表,也就是通过 PRIMARY 索引找到叶子节点中保存的行数据。

    这里可能很多人会有疑问了,uniq_trans_id 已经是一个唯一索引了,通过这个索引最终只能找到最多一条数据,那MySQL优化器为啥还要用两个索引取交集,再回表进行查询呢,这样不是多了一次 idx_status 索引查找的过程么。我们来分析一下这两种情况执行过程。

    第一种 只用uniq_trans_id索引 :

    • 根据 trans_id = ‘38’查询条件,利用uniq_trans_id 索引找到叶子节点中保存的id值;

    • 通过找到的id值,利用PRIMARY索引找到叶子节点中保存的行数据;

    • 再通过 status = 0 条件对找到的行数据进行过滤。

    第二种 用到索引合并 Using intersect(uniq_trans_id,idx_status):

    • 根据 trans_id = ‘38’ 查询条件,利用 uniq_trans_id 索引找到叶子节点中保存的id值;

    • 根据 status = 0 查询条件,利用 idx_status 索引找到叶子节点中保存的id值;

    • 将1/2中找到的id值取交集,然后利用PRIMARY索引找到叶子节点中保存的行数据

    上边两种情况,主要区别在于,第一种是先通过一个索引把数据找到后,再用其它查询条件进行过滤;第二种是先通过两个索引查出的id值取交集,如果取交集后还存在id值,则再去回表将数据取出来。

    当优化器认为第二种情况执行成本比第一种要小时,就会出现索引合并。(生产环境流水表中 status = 0 的数据非常少,这也是优化器考虑用第二种情况的原因之一)。

    为什么用了 index_merge 就死锁了

    MySQL优化:如何解决因index merge引起的死锁?

    上面简要画了一下两个update事务加锁的过程,从图中可以看到,在idx_status 索引和 PRIMARY (聚簇索引) 上都存在重合交叉的部分,这样就为死锁造成了条件。

    如,当遇到以下时序时,就会出现死锁:

    MySQL优化:如何解决因index merge引起的死锁?

    事务1等待事务2释放锁,事务2等待事务1释放锁,这样就造成了死锁。

    MySQL检测到死锁后,会自动回滚代价更低的那个事务,如上边的时序图中,事务1持有的锁比事务2少,则MySQL就将事务1进行了回滚。

    解决方案

    一、从代码层面

    • where 查询条件中,只传 trans_id ,将数据查询出来后,在代码层面判断 status 状态是否为0;

    • 使用 force index(uniq_trans_id) 强制查询语句使用 uniq_trans_id 索引;

    • where 查询条件后边直接用 id 字段,通过主键去更新。

    二、从MySQL层面

    • 删除 idx_status 索引或者建一个包含这俩列的联合索引;

    • 将MySQL优化器的index merge优化关闭。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    编程开发 MySQL
    相关文章 更多
    Claude Code AI编程工具实力揭秘与编程助手实测
    Claude Code AI编程工具实力揭秘与编程助手实测

    通过实测展示Claude Code在终端中如何理解自然语言指令、自动修改代码文件并处理复杂编程任务,帮助开发者评估其实际辅助能力。

    winforms教程自学入门与基础开发步骤详解
    winforms教程自学入门与基础开发步骤详解

    本教程详细讲解如何使用Visual Studio创建WinForms项目,通过添加按钮和标签控件并编写点击事件代码,实现一个基础的计数器功能,适合C#初学者快速上手Windows窗体应用开发。

    Cursor自动补全设置教程教你快速开启代码补全功能
    Cursor自动补全设置教程教你快速开启代码补全功能

    详解Cursor编辑器中自动补全功能的开启与优化设置,涵盖Tab触发机制、上下文窗口调整及模型切换,帮助开发者解决补全延迟、干扰大等问题,提升编码流畅度。

    pandas的数据格式怎么转换和设置方法教程
    pandas的数据格式怎么转换和设置方法教程

    详解Pandas中数据格式转换的核心方法,包括astype强制转换、to_numeric容错处理及日期解析技巧,解决常见类型错误并提升数据处理效率。

    VS Code中文设置方法 简体语言包安装与切换教程
    VS Code中文设置方法 简体语言包安装与切换教程

    详细介绍在Visual Studio Code中安装Chinese (Simplified)语言包的方法,包括通过扩展市场搜索、安装及自动重启切换至简体中文界面的完整步骤,帮助开发者快速将编辑器本地化。

    cursor安装过程无法更改安装位置的解决方法
    cursor安装过程无法更改安装位置的解决方法

    针对Cursor安装包默认锁定C盘且无路径选择界面的问题,提供通过手动移动文件并创建目录联结(Symbolic Link)的解决方案,实现将软件安装在其他磁盘分区。

    rust下载安装教程详解及Windows环境配置方法
    rust下载安装教程详解及Windows环境配置方法

    详解Windows系统下Rust语言的安装步骤,重点解析rustup工具链管理机制,解决环境变量配置错误及MSVC链接器缺失问题,提供可复制的命令验证方法与常见报错的因果排查思路。

    vs code怎么配置 chat实用设置教程步骤
    vs code怎么配置 chat实用设置教程步骤

    详解VS Code中Chat插件的安装与核心配置步骤,重点解决API连接失败、响应慢等常见问题,通过优化上下文设置提升代码生成质量,适合希望集成AI辅助工具的开发者阅读。

    uniapp实例教程代码详解与项目实战指南
    uniapp实例教程代码详解与项目实战指南

    本教程通过实战案例详解UniApp开发流程,包括项目初始化、页面结构解析、数据绑定与事件处理,帮助初学者快速上手跨平台应用开发。

    android studio安装教程2026
    android studio安装教程2026

    详细讲解2026年最新版Android Studio的下载与安装步骤,涵盖JDK环境检查、组件选择及初始配置,助您顺利开启安卓应用开发之旅。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    赤友清理大师
    赤友清理大师
    macOS

    赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    WINDOWS 更多
    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    Windows 10
    Windows 10
    Windows

    Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。