INSERT ... ON DUPLICATE KEY UPDATE 的锁粒度与死锁风险分析
发布时间 - 2026-01-30 00:00:00 点击率:次ON DUPLICATE KEY UPDATE 会锁所有扫描到的唯一索引记录及其间隙,而非仅冲突行;并发插入可能因多唯一索引加锁顺序不一致导致死锁,需通过统一访问顺序、减少唯一约束、捕获死锁后重试来缓解。
ON DUPLICATE KEY UPDATE 会锁哪些行
它不是只锁“将要插入的那条记录”,而是先按 INSERT 路径走:对唯一索引(PRIMARY KEY 或 UNIQUE KEY)匹配的冲突行加 INSERT intention lock,再升级为 X lock;如果没冲突,则对插入位置加 gap lock 或 next-key lock。关键点在于——只要涉及唯一索引查找,InnoDB 就会对**所有扫描到的唯一索引记录及其间隙**加锁,哪怕最终只更新其中一条。
常见误判是认为“只锁冲突行”,实际中:INSERT ... ON DUPLICATE KEY UPDATE 在唯一索引上执行时,会触发和 SELECT ... FOR UPDATE 类似的加锁行为,尤其当唯一索引非主键、或存在多个唯一约束时,锁范围可能意外扩大。
为什么两个并发 INSERT 可能死锁
典型死锁场景:事务 A 和 B 同时执行相同语句,但扫描/加锁顺序不同(比如因索引 B+ 树分裂、页分裂导致遍历路径不一致),或它们分别命中了不同唯一索引(如一个走 uk_email,另一个走 uk_phone),就可能形成循环等待。
- 事务 A 先锁住
uk_email上的某条记录 X,再尝试获取uk_phone上的记录 Y - 事务 B 先锁住
uk_phone上的 Y,再尝试获取uk_email上的 X
MySQL 无法预判这种跨索引的锁依赖,只能在加锁失败时检测并回滚其中一个事务。这类死锁不会报 Lock wait timeout,而是直接返回 Deadlock found when trying to get lock。
UPDATE 部分是否影响锁行为
不影响加锁范围,只影响是否释放锁。无论 UPDATE 子句有没有实际修改字段值(比如 SET status = status),只要语句进入 UPDATE 分支,就会持有被更新行的 X lock 直到事务结束。但注意:UPDATE 中引用的非唯一字段(如普通二级索引列)不会额外加锁,除非该字段出现在 WHERE 条件里且触发了索引扫描。
一个易忽略点: ON DUPLI 的 
UPDATE 部分不支持子查询或函数调用(如 SET ts = NOW() 是允许的,但 SET val = (SELECT ...) 会报错),这限制了部分动态赋值场景,也间接减少了因子查询引入的额外锁。
如何降低死锁概率
核心思路是让并发操作尽可能按相同顺序访问索引,减少不确定性。具体可做:
- 确保
INSERT的值在唯一索引上有稳定排序(例如插入前对 key 做哈希或归一化处理,避免随机字符串导致 B+ 树分裂不可控) - 尽量只定义一个唯一约束(最好是主键),避免多唯一索引交叉加锁
- 用
REPLACE INTO替代?不行——它本质是DELETE + INSERT,锁更重、还可能触发外键级联和触发器,死锁风险更高 - 业务层加分布式锁?过度设计;更轻量的做法是捕获
Deadlock found when trying to get lock后退避重试(指数退避,最多 3 次)
真正难调试的是那些不常复现的间隙锁竞争——比如两个事务恰好落在同一个 gap 区间内插入不同值,又都触发了 ON DUPLICATE 分支,此时锁行为高度依赖当前索引页状态,连 SHOW ENGINE INNODB STATUS 里的 TRANSACTIONS 都不一定能还原全貌。
# mysql
# ai
# 为什么
# sql
# 分布式
# for
# select
# 字符串
# 循环
# delete
# 并发
# 死锁
# 加锁
# 会报
# 锁住
# 重试
# 的是
# 主键
# 就会
# 子句
# 都不
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel全局作用域是什么_Laravel Eloquent Global Scopes应用指南
如何在阿里云完成域名注册与建站?
Android自定义listview布局实现上拉加载下拉刷新功能
php 三元运算符实例详细介绍
Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】
公司门户网站制作流程,华为官网怎么做?
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
昵图网官方站入口 昵图网素材图库官网入口
*服务器网站为何频现安全漏洞?
Laravel API资源类怎么用_Laravel API Resource数据转换
WordPress 子目录安装中正确处理脚本路径的完整指南
Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全
Laravel如何实现用户密码重置功能?(完整流程代码)
Midjourney怎么调整光影效果_Midjourney光影调整方法【指南】
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
焦点电影公司作品,电影焦点结局是什么?
北京网站制作的公司有哪些,北京白云观官方网站?
Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】
Linux网络带宽限制_tc配置实践解析【教程】
七夕网站制作视频,七夕大促活动怎么报名?
Laravel如何使用Gate和Policy进行授权?(权限控制)
Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程
如何在阿里云虚拟主机上快速搭建个人网站?
作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】
国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?
Laravel 419 page expired怎么解决_Laravel CSRF令牌过期处理
昵图网官网入口 昵图网素材平台官方入口
如何快速选择适合个人网站的云服务器配置?
nodejs redis 发布订阅机制封装实现方法及实例代码
如何快速上传建站程序避免常见错误?
Android使用GridView实现日历的简单功能
HTML 中如何正确使用模板变量为元素的 name 属性赋值
如何在 React 中条件性地遍历数组并渲染元素
Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】
Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤
Win11怎样安装网易有道词典_Win11安装词典教程【步骤】
Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南
Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理
UC浏览器如何切换小说阅读源_UC浏览器阅读源切换【方法】
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
如何在橙子建站中快速调整背景颜色?
Laravel与Inertia.js怎么结合_使用Laravel和Inertia构建现代单页应用
微信小程序 require机制详解及实例代码
Laravel中的withCount方法怎么高效统计关联模型数量
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
如何在IIS中配置站点IP、端口及主机头?
高防服务器租用指南:配置选择与快速部署攻略

