Insert into on duplicate key update 死锁问题解析
Insert into on duplicate key update 死锁问题解析
背景
前段时间的需求中有这个么一个场景,每天早上需要通过定时任务到不同的平台拉取一些广告投放的相关数据,涉及的表比较多,数据量也比较大,有的需要全量同步,有的需要增量拉取。对于增量拉取的数据,如果已经存在了的数据则会覆盖原来的数据,保存最新的数据即可。
因为我们的表里基本都设置了唯一索引的,所以我这边也就采用了一个懒人方法,直接使用insert into on duplicate key update 的操作。
insert into t_user_test(no, name) values (6, 'analysis') on duplicate key update name='analytics';
如果没有冲突,则直接插入,如果有冲突,则进行更新。
由于数据量比较大,我们这边采用的是分批的批量插入。
死锁问题
上面的sql语句确实很好用,但是在并发量较大时,多个事务并发执行同一条insert … on duplicate key update … ,容易发生死锁(比如insert的内容相同时),导致操作执行失败。
org.springframework.dao.DeadlockLoserDataAccessException:
### Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
### The error may exist in class path resource [mapper/TestMapper.xml]
### The error may involve com.order.addOrder-Inline
### The error occurred while setting parameters
### SQL:
insert into t_order_test(id,order_id,money,create_time,creator,creator_id) values (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE money= VALUES(money), update_time = now(),modifier = VALUES(creator), modifier_id = VALUES(creator_id)
### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
问题复现
环境准备
MySQL版本:8.0.13
隔离级别:RR(可重复读)
测试表和数据
-- 创建测试表
CREATE TABLE `t_user_test`
(`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`no` int(11) DEFAULT NULL,`name` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`),unique key (`no`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4ROW_FORMAT = DYNAMIC;
-- 测试数据
insert into t_user_test ( `no`, `name`)
values (1, 'test'),(2, 'test'),(3, 'test' ),(5, 'test'),(30, 'test');
复现步骤
步骤一:开启三个会话连接,三个事务
-
连接1,关闭自动提交,开启一个事务,执行sql,但是不提交
set autocommit = 0;begin;insert into t_user_test(no, name) values (6, 'analysis') on duplicate key update name='analytics';commit;查看加锁情况
selectENGINE_LOCK_ID,ENGINE_TRANSACTION_ID,EVENT_ID,OBJECT_NAME,INDEX_NAME,OBJECT_INSTANCE_BEGIN,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

可以看到,当前事务加了3把锁
事务一
第一把锁:表锁 意向排他锁 这把锁的意义在于防止在insert或update时对表进行ddl操作
第二把锁:行锁 间隙锁
第三把锁:行锁 间隙锁
虽然加了两把GAP间隙锁,但是锁住的5-30之间。间隙锁的意义是,防止update的时候,有其他事务往同一个GAP里面插入数据
-
连接2:关闭自动提交,开启一个事务,执行sql,但是不提交
set autocommit = 0;begin;insert into t_user_test(no, name) values (7, 'analysis') on duplicate key update name='analytics';commit;查看加锁情况
selectENGINE_LOCK_ID,ENGINE_TRANSACTION_ID,EVENT_ID,OBJECT_NAME,INDEX_NAME,OBJECT_INSTANCE_BEGIN,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;

事务一
第一把锁:表锁 意向排他锁 这把锁的意义在于防止在insert或update时对表进行ddl操作
第二把锁:行锁 间隙锁
第三把锁:行锁 间隙锁
事务二
第一把锁:表锁 意向排他锁 这把锁的意义在于防止在insert或update时对表进行ddl操作
第二把锁:行锁 间隙锁
第三把锁:行锁 间隙锁,插入意向锁 waiting状态
意向插入锁:当我们当前的的insert语句没有检测到任何语义相冲突的间隙锁的时候,是不会加插入意向锁的。插入意向锁的意义就是为了阻塞当前的事务。因为事务一的间隙锁跟当前事务的插入相冲突了。这就是事务二等待的原因。
-
连接3:关闭自动提交,开启一个事务,执行sql,但是不提交
set autocommit = 0;begin;insert into t_user_test(no, name) values (8, 'analysis') on duplicate key update name='analytics';commit;查看加锁情况
selectENGINE_LOCK_ID,ENGINE_TRANSACTION_ID,EVENT_ID,OBJECT_NAME,INDEX_NAME,OBJECT_INSTANCE_BEGIN,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
事务一
第一把锁:表锁 意向排他锁 这把锁的意义在于防止在insert或update时对表进行ddl操作
第二把锁:行锁 间隙锁
第三把锁:行锁 间隙锁
事务二
第一把锁:表锁 意向排他锁 这把锁的意义在于防止在insert或update时对表进行ddl操作
第二把锁:行锁 间隙锁
第三把锁:行锁 间隙锁,插入意向锁 waiting状态
事务三
第一把锁:表锁 意向排他锁 这把锁的意义在于防止在insert或update时对表进行ddl操作
第二把锁:行锁 间隙锁
第三把锁:行锁 间隙锁,插入意向锁 waiting状态
事务三的锁跟事务二的锁是一样的,而且也是等待状态,但是不确定事务三的插入意向锁检测到的是事务一的间隙锁还是事务二的间隙锁?有知道的下伙伴可以帮忙解答一下。
步骤二:连接1提交事务
连接三,事务三,发生了死锁

查看加锁情况
selectENGINE_LOCK_ID,ENGINE_TRANSACTION_ID,EVENT_ID,OBJECT_NAME,INDEX_NAME,OBJECT_INSTANCE_BEGIN,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA
from performance_schema.data_locks;

我们发现事务二有两把插入意向锁。
死锁的原因:当事务一提交的时候,事务二的插入意向锁检测到事务一已经提交,然后事务二尝试插入,但是事务三也有一把间隙锁,这时候有检测到了事务三有间隙锁,所以事务二有加了一把新的插入意向锁,阻塞事务二的执行。这时候事务二和事务三就形成了一个闭环。事务二等待事务三的间隙锁,事务三等待事务二的间隙锁,所以形成了死锁。
这也间接解释了上面的问题,阻塞事务三执行的是事务二的间隙锁。
死锁的形成
- 事务一在插入的时候会产生一把间隙锁,锁住要插入的间隙
- 事务二在插入的时候,插入时会检测到事务一的间隙锁,所以事务二会加一把插入意向锁,阻塞事务二的插入。
- 事务三在插入的时候,会检测到事务二的间隙锁,然后事务三会加一把意向插入锁,阻塞事务三的执行。
- 事务一提交
- 事务二检测到事务已提交,会尝试插入数据,然后检测到事务三的间隙锁,然后会加一把新的意向插入锁,阻塞事务二的执行。
- 事务三等待事务二的锁,事务二等待事务三的锁,形成了闭环,也就是死锁。
解决方案
避免在并发高的场景下,使用insert into on duplicate key update操作,使用try…catch捕获duplicate异常,然后使用update操作更新数据即可。
相关文章:
Insert into on duplicate key update 死锁问题解析
Insert into on duplicate key update 死锁问题解析 背景 前段时间的需求中有这个么一个场景,每天早上需要通过定时任务到不同的平台拉取一些广告投放的相关数据,涉及的表比较多,数据量也比较大,有的需要全量同步,有…...
Apache Lucene 10 已发布!Lucene 硬件效率改进及其他改进
作者:来自 Elastic Adrien Grand Apache Lucene 10 刚刚发布,重点关注硬件效率!查看主要版本亮点。 Apache Lucene 10 终于发布了!自 Lucene 9.0(于 2021 年 12 月发布,距今已有近 3 年)以来&a…...
【SQL】SQL查询语句
目录 🎄 基本查询语法 ⭐查询多个字段 ⭐设置别名 ⭐去除重复记录 ⭐ 数据准备 ⭐ 案例 🎄 条件查询 ⭐ 语法 ⭐ 案例 🎄 聚合函数 ⭐ 介绍 ⭐ 常见的聚合函数 ⭐ 语法 ⭐ 案例 🎄 分组查询 ⭐ 语法 ⭐ where与having的区…...
AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型(LLM)应用开发平台
AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型(LLM)应用开发平台 目录 AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型(LLM)应用开发平台 一、简单介绍 二、Docker 下载安…...
机器学习摘下诺奖桂冠
前言 近日,2024年诺贝尔物理学奖颁发给了机器学习与神经网络领域的研究者,这是历史上首次出现这样的情况。这项奖项原本只授予对自然现象和物质的物理学研究作出重大贡献的科学家,如今却将全球范围内对机器学习和神经网络的研究和开发作为了一…...
营销邮件软件:提升邮件营销效率必备工具!
营销邮件软件选择技巧?免费高效的邮件营销软件推荐? 如何高效地管理和优化邮件营销活动成为了企业面临的一大挑战。营销邮件软件成为提升邮件营销效率的必备工具。MailBing将深入探讨营销邮件软件的功能、优势以及如何选择合适的工具。 营销邮件软件&a…...
鸿蒙开发 四十五 鸿蒙状态管理(嵌套对象界面更新)
当运行时的状态变量变化,UI重新渲染,在ArkUI中称为状态管理机制,前提是变量必须被装饰器修饰。不是状态变量的所有更改都会引起刷新,只有可以被框架观测到的更改才会引起UI刷新。其中boolen、string、number类型,可观察…...
第 6 章:vue-router
1. router 相关理解 1.1 vue-router 的理解 vue 的一个插件库,专门用来实现 SPA 应用 1.2 对 SPA 应用的理解 单页 Web 应用(single page web application,SPA)。整个应用只有一个完整的页面。点击页面中的导航链接不会刷新页…...
PaddleOCR模型转换、部署全流程(Ubuntu系统)_随记2
本篇衔接文章1、环境流程需要看随记1就可以 PaddleOCR环境搭建、模型训练、推理、部署全流程(Ubuntu系统)_随记1 一、ONNX导出 1、环境准备 主要参考官方技术文档:官方技术文档 未完做完更新... 参考:PaddleOCR-PP-OCRv4推理详解…...
Tableau 2024.3 发布!表格可视化项扩展、空间参数和 Cloud 管理器等,助力企业大规模分析
在升级至最新版前,先来详细一览 Tableau 2024.2 的最新特性吧~ Tableau 发布新版本啦!作为今年的收官之作,Tableau 2024.3 在延续经典之余,也为用户带来了不少惊喜,让企业数据分析之旅更加丰富多彩。 使用 Tableau Cl…...
即时通讯增加kafka渠道
此次给im服务增加kafka渠道,刚好最近有对SpringCloudStream进行了解,刚好用来练练手 增加kafka渠道 pom.xml 引入stream相关依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-strea…...
建造者模式和工厂模式的区别
工厂模式和建造者模式都是创建型设计模式,它们的主要作用都是为了简化对象的创建过程,但是它们在设计意图和实现细节上有着显著的区别。 总结区别: 关注点不同: 工厂模式关注的是对象的创建。建造者模式关注的是对象的构造过程…...
GEE数据集——ERA5-陆地每日汇总--ECMWF气候再分析数据集
目录 简介 数据集说明 Dataset Availability Dataset Provider Collection Snippet 空间信息 Resolution Bands Table 变量 代码 代码链接 结果 引用 许可 网址推荐 0代码在线构建地图应用 机器学习 简介 注(2024-04-19): …...
Spring Boot 中的 @RequestMapping 和 Spring 中的 @RequestMapping 有什么区别?
在Spring框架中,RequestMapping注解用于映射Web请求到处理器(Controller)的方法上。在Spring Boot中,这个注解的使用方式和目的与Spring框架中是完全相同的。RequestMapping注解可以用于类或方法上,以声明请求的映射。…...
PROFINET开发或EtherNet/IP开发嵌入式归一板有用于工业称重秤
这是真实案例。然而,客户选择不展示其品牌名称。 Anybus嵌入式解决方案帮助工业称重设备制造商连接到任何工业网络。多网络连接使称重设备能够轻松访问不同的控制系统,从而加快上市时间。 我们最终找到了HMSNetworks的Anybus解决方案。他们的成熟技术和专…...
【Kafka】Kafka源码解析之producer过程解读
从本篇开始 打算用三篇文章 分别介绍下Producer生产消费,Consumer消费消息 以及Spring是如何集成Kafka 三部分,致于对于Broker的源码解析,因为是scala语言写的,暂时不打算进行学习分享。 总体介绍 clients : 保存的是Kafka客户端…...
深度学习笔记20_数据增强
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制 一、我的环境 1.语言环境:Python 3.9 2.编译器:Pycharm 3.深度学习环境:TensorFlow 2.10.0 二、GPU设置…...
模板变量与php变量对比做判断
${item.create_name}如何与php变量对比 在PHP中,您可以通过将字符串内嵌到双引号中来将模板变量 ${item.create_name} 与PHP变量进行对比。如果您有一个PHP变量 $phpVariable 并且想要检查它是否与 ${item.create_name} 相同,您可以使用 str_replace 函…...
C语言 | Leetcode C语言题解之第485题最大连续1的个数
题目: 题解: int findMaxConsecutiveOnes(int* nums, int numsSize) {int maxCount 0, count 0;for (int i 0; i < numsSize; i) {if (nums[i] 1) {count;} else {maxCount fmax(maxCount, count);count 0;}}maxCount fmax(maxCount, count);…...
C语言复习概要(六)
公主请阅 1. 深入理解数组与指针在C语言中的应用1.1 数组名的理解 2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序的实现5. 二级指针6. 指针数组7. 指针数组模拟二维数组8.总结 1. 深入理解数组与指针在C语言中的应用 数组与指针是C语言的核心概念之一,理解…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
2.2.2 ASPICE的需求分析
ASPICE的需求分析是汽车软件开发过程中至关重要的一环,它涉及到对需求进行详细分析、验证和确认,以确保软件产品能够满足客户和用户的需求。在ASPICE中,需求分析的关键步骤包括: 需求细化:将从需求收集阶段获得的高层需…...
华硕电脑,全新的超频方式,无需进入BIOS
想要追求更佳性能释放 或探索更多可玩性的小伙伴, 可能会需要为你的电脑超频。 但我们常用的不论是BIOS里的超频, 还是Armoury Crate奥创智控中心超频, 每次调节都要重启,有点麻烦。 TurboV Core 全新的超频方案来了 4不规…...
【Redis】Redis 的持久化策略
目录 一、RDB 定期备份 1.2 触发方式 1.2.1 手动触发 1.2.2.1 自动触发 RDB 持久化机制的场景 1.2.2.2 检查是否触发 1.2.2.3 线上运维配置 1.3 检索工具 1.4 RDB 备份实现原理 1.5 禁用 RDB 快照 1.6 RDB 优缺点分析 二、AOF 实时备份 2.1 配置文件解析 2.2 开启…...
