MySQL的index merge(索引合并)导致数据库死锁分析与解决方案 | 京东云技术团队

背景

在DBS-集群列表-更多-连接查询-死锁中,看到9月22日有数据库死锁日志,后排查发现是因为mysql的优化-index merge(索引合并)导致数据库死锁。

定义

index merge(索引合并):该数据库查询优化的一种技术,在mysql 5.1之后进行引入,它可以在多个索引上进行查询,并将结果合并返回。

mysql数据库的锁机制

在排查问题之前,首先讲一下mysql数据库的锁机制:

1 加锁的基本单位是 next-key lock(记录锁+间隙锁),当记录锁或者间隙锁能够解决幻读的问题,就会退化为记录锁(行锁),间隙锁。

2 加锁是将锁加在了索引之上,而不是数据之上。

3 对于当前读,索引进行加锁,当前读语句包括了(select … from. … for update,select…from … lock in share mode,update…,delete…)。

4 加锁根据唯一性索引、非唯一性索引进行了区分,根据查询条件分为了等值查询、范围查询,根据是否能够查到数据又分为了记录存在和不存在的情况。

本次死锁问题使用的索引是非唯一性索引的等值查询中记录存在的情况,因此本文仅仅详细介绍这种情况,其它情况可以查看最下面的参考文档1:

加锁情况是:会依次扫描,首先扫描到条件匹配的数据,加一个next-key lock,然后接下来扫描到第一个记录不匹配的数据,增加一个间隙锁,最后对查到记录的主键增加一个记录锁,

针对以上情况加了三种锁,加锁的目的是为了防止幻读的发生。

针对二级索引的锁进行分析:

表结构:

CREATE TABLE `jdi_roster_apply_detail` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`apply_id` varchar(100) NOT NULL COMMENT '申请单号',`status` tinyint(10) NOT NULL COMMENT '状态',PRIMARY KEY (`id`),KEY `idx_status` (`status`),KEY `idx_apply_id` (`apply_id`)
) ENGINE=InnoDB AUTO_INCREMENT=984483 DEFAULT CHARSET=utf8 COMMENT='黑白名单申请单明细'

表数据:

idapply_idstatus
95965116953692205220689981
96073816953692275761736901
96131916953730476739033261
96136516953731224478652281

通过 idx_apply_id建立的b+树:

因为索引是二级索引,所以叶子节点存储的数据是主键值。

执行sql:

select * from jdi_roster_apply_detail where apply_id='1695369227576173690' for update

执行数据扫描过程

1 查到符合条件的记录,增加next-key 锁,因此锁是(1695369220522068998,1695369227576173690]

2 找到第一个不符合记录的数据增加间隙锁,因此锁是 (1695369227576173690,1695373047673903326)

3 对符合条件的主键索引增加记录锁,因此对 id=960738,增加记录锁。

针对三种锁解决的幻读:

1 如果没有第一条的next-key锁, 另一个事务增加一个apply_id=1695369227576173690, id<960738 时,该事务在进行查询时,会多一条记录,因此会造成幻读。

2 如果没有第二条的 间隙锁,另一个事务增加一个apply_id=1695369227576173690, id>960738是,该事务在进行查询时,会多一条记录,因此会造成幻读。

3 如果没有第三条的记录锁,另一条事务删除一条 id=960738的记录,该事务进行查询时,会少一条数据,因此会造成幻读。

实际问题分析

数据库死锁日志

以上日志两个事务分别执行了update语句:

#事务1
update jdi_roster_apply_detail set `status` = 10 where `status` = 1 and apply_id = '1695369220522068998'
#事务2
update jdi_roster_apply_detail set `status` = 10 where `status` = 1 and apply_id = '1695369227576173690' 

这个sql是用于将某个申请单id待审批的数据改为已审批。

因为在泰山里不能执行update语句 ,因此执行了select语句查看用的索引情况:

explain select * from  jdi_roster_apply_detail  where `status` = 1 and apply_id = '1695369220522068998'

执行的结果:

通过结果可以看出两个update语句都使用了两个索引,分别是idx_status,idx_apply_id,然后将查到的结果进行合并,因此在模拟的过程中,可以将其拆成两个查询语句。

死锁模拟

事务1事务2锁的范围
beginbegin
select * from jdi_roster_apply_detail where apply_id = ‘1695369220522068998’ for updateidx_apply_id所以锁住了(-∞,1695369220522068998],(1695369220522068998,1695369227576173690) 主键id索引锁住了 id=959651
select * from jdi_roster_apply_detail where apply_id = ‘1695369227576173690’ for updateidx_apply_id所以锁住了(1695369220522068998,1695369227576173690],(1695369227576173690,1695373047673903326) 主键id索引锁住了 id=960738
select * from jdi_roster_apply_detail where status = 1 for update会对idx_status上加next-key锁和间隙锁,但是在对主键959651,960738,961319,961365进行加记录锁时,其中事务2 对960738已经加了记录锁,所以该事务1进行了阻塞。
select * from jdi_roster_apply_detail where status = 1 for update会对idx_status上加next-key锁和间隙锁,但是在对主键959651,960738,961319,961365进行加记录锁时,其中事务1对959651已经加了记录锁,所以该事务2进行了阻塞。
deadlock

两个事务分别想要两个主键id的记录锁,造成相互等待,形成了死锁。

以上是先执行idx_apply_id的索引查询再执行idx_status索引查询,如果先执行idx_status索引查询,再执行idx_apply_id的索引查询,也会因为主键的记录锁造成死锁。

解决方案

1 利用force index(idx_apply_id)强制走某个索引,这样InnoDB就会忽略index merge,避免多个索引同时加锁的情况。

2 禁用Index Merge,用命令禁用Index Merge:SET GLOBAL optimizer_switch=‘index_merge=off,index_merge_union=off,index_merge_sort_union=off,index_merge_intersection=off’;

3 Index Merge同时使用了2个独立索引,因此新建一个包含这两个索引所有字段的联合索引,这样InnoDB就只会走这个单独的联合索引。

第三种方案相较于第一种查询性能更好,相对于第二种仅仅作用于该表,影响范围小,因此本次也是采用了该方案。

总结

该死锁问题是因为优化器使用了合并索引问题导致的,最终通过新建一个联合索引来解决这个问题。

参考文档:

1 https://www.xiaolincoding.com/mysql/lock/how_to_lock.html

作者:京东工业 李小辉

来源:京东云开发者社区 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/160196.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JOSEF约瑟 漏电继电器 JD1-200 工作电压:380V 孔径:45mm 50~500mA

JD1系列漏电继电器 系列型号 JD1-100漏电继电器 JD1-200漏电继电器 JD1-250漏电继电器 JD1系列漏电继电器原为分体式固定式安装&#xff0c;为适应现行安装场合需要&#xff0c;上海约瑟继电器厂在产品原JD1一体式漏电继电器基础上进行产品升级&#xff0c;开发出现在较为…

从零开始学习调用百度地图网页API:二、初始化地图,鼠标交互创建信息窗口

目录 代码结构headbodyscript 调试 代码 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><meta name"viewport" content"initial-scale1.0, user-scalable…

黑马JVM总结(三十四)

&#xff08;1&#xff09;JMM概述 &#xff08;2&#xff09;JMM-原子性-synchronized java内存模型是如何保证原子性的呢&#xff0c;它是通过synchroized关键字&#xff0c;来达到这个目的的 第一个线程来了进入同步代码块之后&#xff0c;把这个对象加上锁了&#xff0c;…

LockSupport-LockSupport是什么及等待唤醒机制对比

4.2 LockSupport是什么 LockSupport是用来创建锁和其他同步类的基本线程阻塞原语&#xff0c;其中park()和unpack()而作用分别是 阻塞线程和解除阻塞线程. 4.3 线程等待唤醒机制 4.3.1 三种让线程等待和唤醒的方法 方式一&#xff1a;使用Object中的wait()方法让线程等待&a…

【广州华锐互动】人体血管器官3D动态展示为医学生提供哪些便利?

人体血管器官3D动态展示是一种采用先进的计算机图形技术和立体成像技术&#xff0c;对人体内部结构和功能进行三维可视化的教学方法。这种教学方式以其独特的优势&#xff0c;正在改变传统的解剖学教学模式&#xff0c;为医学教育带来了革新。 首先&#xff0c;3D动态演示能够提…

ROS系列(二):rosbag 中提取视频数据

一、环境安装 当前环境在上一篇文章的基础上进行配置。 ROS系列&#xff08;一&#xff09;&#xff1a;【环境配置】rosbag 包安装_安装rosbag-CSDN博客 继续安装 sudo apt install ffmpeg python 包如下 pip install sensor_msgs --extra-index-url https://rospypi.gi…

互联网Java工程师面试题·Java 总结篇·第三弹

20、重载&#xff08;Overload&#xff09;和重写&#xff08;Override&#xff09;的区别。重载的方法能否根据返回类型进行区分&#xff1f; 方法的重载和重写都是实现多态的方式&#xff0c;区别在于前者实现的是编译时的多态性&#xff0c;而后者实现的是运行时的多态性。重…

ROS opencv 人脸识别

人脸识别需要在输入的图像中确定人脸&#xff08;如果存在&#xff09;的位置、大小和姿态&#xff0c;往往用于生物特征识别、视频监听、人机交互等应用中。2001年&#xff0c;Viola和Jones提出了基于Haar特征的级联分类器对象检测算法&#xff0c;并在2002年由Lienhart和Mayd…

Linux安装rpm包在线安装mysql5.7

以前安装过mysql 前言&#xff1a;检查以前是否装有mysql rpm -qa|grep -i mysql安装了会显示&#xff1a;   bt-mysql57-5.7.31-1.el7.x86_64 停止mysql服务和删除之前安装的mysql rpm -e bt-mysql57-5.7.31-1.el7.x86_64查找并删除mysql相关目录 find / -name mysql/va…

基础课2——自然语言处理

1.概念 自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是计算机科学领域与人工智能领域中的一个重要方向&#xff0c;它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。 自然语言处理的主要研究方向包括&#xff1a; 语言学研究&…

【版本控制】Git(学习笔记)

一、Git工作流程图 clone&#xff08;克隆&#xff09;: 从远程仓库中克隆代码到本地仓库checkout &#xff08;检出&#xff09;&#xff1a;从本地仓库中检出一个仓库分支然后进行修订add&#xff08;添加&#xff09;: 在提交前先将代码提交到暂存区commit&#xff08;提交&…

AI算法检测对无人军用车辆的MitM攻击

南澳大利亚大学和查尔斯特大学的教授开发了一种算法来检测和拦截对无人军事机器人的中间人&#xff08;MitM&#xff09;攻击。 MitM 攻击是一种网络攻击&#xff0c;其中两方&#xff08;在本例中为机器人及其合法控制器&#xff09;之间的数据流量被拦截&#xff0c;以窃听或…

css3自动吸附scroll-snap

我们希望可以一块一块的滚动&#xff0c;比如当前一个块滚出去了一部分并且后一个块滚进来一部分的时候&#xff0c;实现后一个块自动滚入或者前一个块回弹到初始位置这种效果&#xff0c;以前的时候用js需要写比较复杂的判断逻辑&#xff0c;后来有了一个css scroll snap这个方…

C# Winform编程(2)常用控件

C# Winform编程&#xff08;2&#xff09;常用控件 常用控件 常用控件 标签&#xff0c;文本&#xff0c;按钮&#xff0c;列表框&#xff0c;组合框等的使用 Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks…

apache shiro安全框架反序列化漏洞

用linux搭建一个环境 配置下源vi /etc/apt/sources.list 源如果是kali官方的有时候会下载不了&#xff0c;改成中科大的源 更新下源apt-get update 安装docker-compose 重启kali 启动docker容器 apt-get install docker apt-get install docker-compose reboot service do…

Prometheus的Pushgateway快速部署及使用

prometheus-pushgateway安装 一. Pushgateway简介 Pushgateway为Prometheus整体监控方案的功能组件之一&#xff0c;并做于一个独立的工具存在。它主要用于Prometheus无法直接拿到监控指标的场景&#xff0c;如监控源位于防火墙之后&#xff0c;Prometheus无法穿透防火墙&…

Xilinx IP 10G Ethernet PCS/PMA IP Core

Vivado 10G Ethernet PCS/PMA介绍 1介绍 完整的10G以太网接口如下图,分为10G PHY和10G MAC两部分。 这篇文章重点讲 10G Ethernet PCS/PMA。 2 IP的基本介绍 10G以太网物理编码子层/物理介质连接(PCS/PMA)核心在Xilinx 10G以太网介质访问控制器(MAC)核心和具有10Gb/s…

Maven打包添加本地工程jar包

前言 先吐槽几句,公司有一小组专门来做各个项目的测试环境以及打包上线的工作&#xff0c;我们称之为XX,这个XX并不是什么业务领导&#xff0c;也只是一个螺丝钉。这群人每天对上跪舔&#xff0c;对其他人爱搭不理&#xff0c;给人一种高高在上的感觉&#xff0c;之前的一个老…

【3】c++11新特性(稳定性和兼容性)—>类成员的快速初始化

在进行类成员变量初始化的时候&#xff0c;C11标准对于C98做了补充&#xff0c;允许在定义类的时候在类的内部直接对非静态变量进行初始化&#xff0c;在初始化的时候可以使用等号&#xff0c;也可以使用花括号{}&#xff0c;等号可以省略不写&#xff1b;静态成员变量需要在类…

OpenCV16-图像连通域分析

OpenCV16-图像连通域分析 1.图像连通域分析2.connectedComponents3.connectedComponentsWithStatus 1.图像连通域分析 连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域。连通域分析是指在图像中寻找彼此互相独立的连通域并将其标记出来。 4邻域与8邻域的概念&am…