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语言的核心概念之一,理解…...
保姆级教程:用iSYSTEM winIDEA和iC5000给S32K148烧录程序,附完整配置流程
从零掌握iSYSTEM工具链:S32K148开发板烧录与调试全流程实战第一次接触iSYSTEM的winIDEA和iC5000仿真器时,很多嵌入式开发者都会感到无从下手。不同于常见的开源工具链,这套专业级开发环境在汽车电子和工业控制领域有着广泛应用,尤…...
C++中显示与隐式加载dll的使用与区别
一、什么是 DLL?DLL(Dynamic Link Library) 是 Windows 下的动态链接库,包含可被多个程序共享的函数、资源或类。使用 DLL 可以实现代码复用、模块化设计和插件机制。在 C 中,调用 DLL 中的函数有两种主要方式…...
ARM指令追踪技术及TRCVICTLR寄存器详解
1. ARM指令追踪技术概述在嵌入式系统开发和调试过程中,指令追踪(Instruction Trace)是一项至关重要的技术。它通过硬件机制记录处理器的执行流程,为开发者提供程序运行的完整轨迹。ARM架构从v7开始引入嵌入式跟踪宏单元࿰…...
零基础轻松拿捏!魔珐星云青少年健康运动教学数字人搭建全流程指南
大家好!本次给大家分享一款面向青少年体育教育的AI创意实践项目——青少年健康运动教学智能数字交互系统。本项目聚焦青少年体质健康痛点,围绕体育教学智能化升级需求,打造集健康知识教学、运动动作陪练、健康知识考核、运动能力评测于一体的…...
用STM32CubeMX和HAL库快速上手WS2812B:告别手动计算延时,一键生成驱动框架
基于STM32CubeMX的WS2812B智能灯光控制:从零构建现代化驱动方案在智能硬件和物联网设备快速发展的今天,WS2812B可编程LED灯带因其丰富的色彩表现和简单的单线控制方式,成为创客和工程师们最喜爱的显示组件之一。然而,传统的寄存器…...
收藏必看|2026 版大厂 AI 岗位薪资曝光!普通程序员转型大模型最全指南
深夜收到大厂 HR 好友发来的内部资料,再三叮嘱切勿对外泄露。如今网络信息传播速度极快,这份 2026 年企业 AI 岗真实薪资内幕,也值得给广大程序员、零基础入行小白参考借鉴。 翻看完整薪资台账后,真切感受到当下大模型赛道的薪资差…...
开发转兼职DBA(二):执行计划教我做事
开发转兼职DBA(二):执行计划教我做事 查询慢了不知道为什么,加了索引还是慢,复合索引怎么建,执行计划怎么看——这些不是DBA的专利,是每个写SQL的开发者迟早要面对的事。 文章目录 开发转兼职DB…...
翻译 GDB 官方文档
翻译 GDB 官方文档项目地址官方文档地址下载源码包编译html运行翻译程序项目地址 https://github.com/shootercheng/gdb-translate.git 项目结构 $ tree -L 1 . ├── cmd ├── go.mod ├── input ├── internal ├── LICENSE ├── output ├── README.md ├─…...
别再死磕USB HID了!用ESP32的Arduino框架手把手教你实现蓝牙鼠标键盘(附完整代码)
ESP32蓝牙HID实战:零基础打造自定义键盘鼠标 手里那块吃灰的ESP32开发板终于能派上用场了!上周我用它做了个无线演示控制器,在会议室里走着就能翻PPT,同事们都问是怎么实现的。其实秘诀就在于ESP32的蓝牙HID功能——不需要任何USB…...
Raspberry Pi Debug Probe:RP2040嵌入式开发的调试利器与实战指南
1. 项目概述:为什么你需要一个Raspberry Pi Debug Probe?如果你玩过树莓派Pico或者任何基于RP2040芯片的开发板,肯定遇到过这样的场景:写好的代码,点一下“上传”,然后……就没有然后了。板子上的LED没按你…...
