唯一索引插入引发的死锁问题

news2024/9/20 22:23:01

MySQL 8.4.0

RR隔离级别

场景复现

分析下面SQL死锁的场景

对于switch表,有主键索引id和唯一索引(uid、type)。

 CREATE TABLE `switch` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'Unique identifier for each switch',
  `uid` int NOT NULL COMMENT 'User ID associated with the switch',
  `type` tinyint NOT NULL COMMENT 'Type of the switch',
  `val` tinyint(1) NOT NULL COMMENT 'Value of the switch, TRUE for on and FALSE for off',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uid` (`uid`,`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Table to store switch information for different users'

业务中有这么一个需求,以事务的方式批量插入固定两个开关;

注意,虽然是批量插入,但顺序是固定的!!!

INSERT INTO switch (uid, type, val) VALUES  (30001,3,1) ON DUPLICATE KEY UPDATE val = VALUES(val);
INSERT INTO switch (uid, type, val) VALUES  (30001,23,0) ON DUPLICATE KEY UPDATE val = VALUES(val);

然后业务上线后一段时间,告警发现事务出现了死锁Deadlock found when trying to get lock; try restarting transaction。

在这里插入图片描述

死锁排查

排除乱序插入;当时以为是事务中批量插入的顺序不一致,但重新梳理了下业务,能确定事务插入顺序都是固定的【23、3】;

INSERT INTO switch (uid, type, val) VALUES  (30001,23,0) ON DUPLICATE KEY UPDATE val = VALUES(val);
INSERT INTO switch (uid, type, val) VALUES  (30001,3,0) ON DUPLICATE KEY UPDATE val = VALUES(val);

唯一索引顺序不一致:后面想到使用了唯一索引,唯一索引的顺序是按照uid、type递增排序的,但我插入的时候是先插入type=23、再插入type=3的记录,因此怀疑是插入的时候,唯一索引上加了间隙锁,锁住了(0~23)的间隙,两个事务都保持间隙锁不释放,就产生了死锁。

定位到问题后,尝试把插入顺序调整为唯一索引的顺序,再尝试,就没有死锁问题了。
在这里插入图片描述

锁分析

导出死锁日志:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-07-13 03:56:02 281472325639936
*** (1) TRANSACTION:
TRANSACTION 6770, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1128, 1 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 281472766701312, query id 100 localhost root update
INSERT INTO switch (uid, type, val) VALUES  (30001,23,0) ON DUPLICATE KEY UPDATE val = VALUES(val)

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 7 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6770 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6770 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;


*** (2) TRANSACTION:
TRANSACTION 6769, ACTIVE 9 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 2
MySQL thread id 10, OS thread handle 281472765583104, query id 101 localhost root update
INSERT INTO switch (uid, type, val) VALUES  (30001,3,0) ON DUPLICATE KEY UPDATE val = VALUES(val)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 7 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6769 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6769 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

使用GPT分析,

  1. 死锁的原因
    • 两个事务都试图在mydatabase.switch表的uid索引上插入或更新具有相同uid(在这里是u1,即十六进制的80007531)的记录。
    • 事务6770(Thread ID 9)正在等待一个它当前也持有的锁(这是一个死锁的经典标志)。
    • 事务6769(Thread ID 10)正在等待一个间隙锁(gap lock)以插入一个新的记录,但同时它也持有一个对已有记录的记录锁(record lock)。
  2. 详细分析
    • 事务6770正在尝试插入一个记录(或更新一个已存在的记录),其uidu1,并且它正在等待一个X(排他)锁来允许这个操作。然而,它已经在等待该锁,因为它也被事务6769持有。
    • 事务6769也在尝试插入或更新同一个uidu1的记录。它已经持有一个对u1记录的X锁,但它现在还需要一个间隙锁(gap lock)来确保在u1之前或之后的间隙中没有其他记录被插入,这样它就可以安全地插入一个新的uidu1的记录(即使它实际上并不打算插入一个新的uid,但由于使用了INSERT ... ON DUPLICATE KEY UPDATE语法,MySQL仍然需要确保间隙的锁)。
    • 因为两个事务都在等待对方释放锁,所以形成了一个死锁。

解决方案

那为什么让插入顺序和唯一索引的顺序一致后,就没有死锁产生了呢?

那是因为,如果插入操作不会导致间隙中出现新的索引值,MySQL可以优化并使用行级锁,而不会产生额外的间隙锁。

继续分析上面解决方案中事务的死锁报告:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-07-13 04:10:58 281472325639936
*** (1) TRANSACTION:
TRANSACTION 6835, ACTIVE 5 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1128, 1 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 281472766701312, query id 126 localhost root update
INSERT INTO switch (uid, type, val) VALUES  (30001,23,0)

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 10 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6835 lock mode S waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6835 lock mode S waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;


*** (2) TRANSACTION:
TRANSACTION 6834, ACTIVE 7 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 2
MySQL thread id 10, OS thread handle 281472765583104, query id 127 localhost root update
INSERT INTO switch (uid, type, val) VALUES  (30001,3,0)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 10 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6834 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10 page no 5 n bits 72 index uid of table `mydatabase`.`switch` trx id 6834 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80007531; asc   u1;;
 1: len 1; hex 97; asc  ;;
 2: len 4; hex 80000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

从您提供的死锁报告来看,虽然两个事务(Transaction 6835 和 Transaction 6834)都在尝试操作相同的uidu1,即十六进制的80007531)在mydatabase.switch表的uid索引上,但是实际上并没有形成经典的死锁情况。

这里的关键点在于:

  1. Transaction 6835(Thread ID 9)正在等待一个S(共享)锁,但它已经显示为waiting状态,这意味着它并没有实际持有任何锁,而只是在等待。在MySQL的InnoDB存储引擎中,共享锁通常允许多个事务同时读取同一行,但不允许其他事务修改它。然而,由于Transaction 6835处于等待状态,它实际上并没有持有任何锁。
  2. Transaction 6834(Thread ID 10)已经持有一个X(排他)锁在uidu1的记录上,并正在等待一个间隙锁(gap lock)以允许它插入一个可能的新记录(尽管实际上由于ON DUPLICATE KEY UPDATE,它可能只是更新现有的记录)。间隙锁用于锁定一个范围,但不包括记录本身,以确保没有其他事务在该范围内插入新记录。

由于Transaction 6835没有实际持有任何锁,它只是在等待一个它还没有获得的锁,因此它不能与Transaction 6834形成死锁。死锁通常发生在两个或更多的事务互相等待对方释放资源时。

可以发现,事务6835因为是顺序插入type=3、23的记录,并不需要间隙锁,所以不会和事务6834持有的X锁形成循环等待关系。

总结

在批量插入的过程中,尽量保证插入顺序与索引的顺序一致,不仅是主键索引,也包括唯一索引。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Autoware 定位之基于ARTag的landmark定位(六)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务,并且需要GPU资源,可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU,按时收费每卡2.6元,月卡只需要1.7元每小时&…

AI大模型来了,低代码还有机会吗?

AI大模型的突飞猛进,不仅引领了技术的革新浪潮,也为各行各业的发展带来了前所未有的挑战与机遇。近年来,随着人工智能技术的不断进步,关于各行各业将被AI取代的论调此起彼伏,引发了许多从业者的不安。 几年前&#xf…

Flowable-流程图标与流程演示

BPMN 2.0是业务流程建模符号2.0的缩写。它由Business Process Management Initiative这个非营利协会创建并不断发展。作为一种标识,BPMN 2.0是使用一些符号来明确业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。目前BPMN2.0是最新的…

AI人工智能填词,唱响心中独特旋律

在音乐的无垠宇宙中,每个人的内心都有一段独一无二的旋律在悄然回荡。而如今,人工智能填词正以其神奇的力量,帮助我们将这些深藏心底的旋律化作动人的歌词,让它们得以放声歌唱。 “妙笔生词智能写歌词软件(veve522&am…

【QT】布局管理器

布局管理器 布局管理器1. 垂直布局2. 水平布局3. 网格布局4. 表单布局5. Spacer 布局管理器 之前使⽤ Qt 在界⾯上创建的控件, 都是通过 “绝对定位” 的⽅式来设定的;也就是每个控件所在的位置, 都需要计算坐标, 最终通过 setGeometry 或者 move ⽅式摆放过去。 …

一文彻底学会Vue3路由:全面讲解路由流程、路由模式、传参等——全栈开发之路--前端篇(7)路由详解

全栈开发一条龙——前端篇 第一篇:框架确定、ide设置与项目创建 第二篇:介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇:setup语法,设置响应式数据。 第四篇:数据绑定、计算属性和watch监视 第五篇 : 组件…

pytorch-pytorch之LSTM

目录 1. nn.LSTM2. nn.LSTMCell 1. nn.LSTM 初始化函数输入参数与RNN相同,分别是input_size,hidden_size和num_layer foward函数也与RNN类似,只不过返回值除了out外,ht变为(ht,ct) 代码见下图: 2. nn.LSTMCell 初…

SQL优化-索引

什么是索引? 索引( index )是帮助 MySQL 高效获取数据的数据结构 ( 有序 ) 。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这…

这是我见过最棒的大模型干货!!!

大模型技术的发展和迭代2024年已经可以按天来计算了,几乎每天都有新的大模型和技术登场。 从基座模型Mamba2,Jamaba,到Dora,LoftQ,GaLore等最新的微调技术;KTO,IPO,SimPO等微调技术…

STM32实战篇:按键(外部输入信号)触发中断

功能要求 将两个按键分别与引脚PA0、PA1相连接,通过按键按下,能够触发中断响应程序(不需明确功能)。 代码流程如下: 实现代码 #include "stm32f10x.h" // Device headerint main() {//开…

ZGC的流程图

GC标记过程 1、初始标记 扫描所有线程栈的根节点,然后再扫描根节点直接引用的对象并进行标记。这个阶段需要停顿所有的应用线程(STW),但由于只扫描根对象直接引用的对象,所以停顿时间很短。停顿时间高度依赖根节点的数…

鸿蒙HarmonyOS应用开发为何选择ArkTS不是Java?

前言 随着智能设备的快速发展,操作系统的需求也变得越来越多样化。为了满足不同设备的需求,华为推出了鸿蒙HarmonyOS。 与传统的操作系统不同,HarmonyOS采用了一种新的开发语言——ArkTS。 但是,刚推出鸿蒙系统的时候&#xff0…

uni-app 保存号码到通讯录

1、 添加模块 2、添加权限 3、添加策略 Android: "permissionExternalStorage" : {"request" : "none","prompt" : "应用保存运行状态等信息,需要获取读写手机存储(系统提示为访问设备上的照片…

Prometheus + alermanager + webhook-dingtalk 告警

添加钉钉机器人 1. 部署 alermanager 1.1 下载软件包 wget https://github.com/prometheus/alertmanager/releases/download/v0.26.0/alertmanager-0.26.0.linux-amd64.tar.gz 网址 :Releases prometheus/alertmanager (github.com) 1.2 解压软件包 mkdir -pv …

用 Kotlin 编写四则运算计算器:从零开始的简单教程

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…

电-气阀门定位器YT-1000系列产品说明

电-气阀门定位器YT-1000系列 使用注意事项 • 搬运安装或使用中对产品过大的震动或撞击会成为产品故障的原因。 • 超过规定参数范围使用也会成为产品故陷的原因。 • 不使用的气路接口要用堵塞堵住。 • 不使用产品而长时间放悝在室外时,要盖上产品外壳以免雨水进入产品…

八款主流电脑监控软件推荐|2024年最佳电脑监控软件排行榜

在现代社会中,电脑监控软件已经成为企业和家庭不可或缺的工具。无论是为了确保员工的工作效率,还是保护孩子在互联网上的安全,这些软件都能提供有力的支持。本文将为大家介绍2024年最受欢迎的八款电脑监控软件。 1. 固信软件 固信软件是一款综…

服务重启时容器未自动启动

1、容器重启策略 通过设置容器的重启策略,‌可以决定在容器退出时Docker守护进程是否重启该容器。‌常见的重启策略包括:‌ no:‌不重启容器,‌默认策略。‌always:‌无论容器是如何退出的,‌总是重启容器…

2024年公共文化与社会服务国际会议(ICPCSS 2024)

2024年公共文化与社会服务国际会议 2024 International Conference on Public Culture and Social Services 【1】会议简介 2024年公共文化与社会服务国际会议是一个集学术性、实践性和国际性于一体的盛会。我们期待与您共同探讨公共文化与社会服务的未来发展方向,为…

【公益案例展】华为云X《无尽攀登》——攀登不停,向上而行

‍ 华为云公益案例 本项目案例由华为云投递并参与数据猿与上海大数据联盟联合推出的 #榜样的力量# 《2024中国数据智能产业最具社会责任感企业》榜单/奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 夏伯渝,中国无腿登珠峰第一人,一生43年…