在高并发环境下SQL执行INSERT时如何防止死锁?

夏辰大大_6069

夏辰大大_6069

2026-07-17

912人浏览

原创

空表并发INSERT必死锁是RR级别下Next-Key Lock导致所有事务在(-∞,+∞)间隙争抢Insert Intention Lock而形成循环等待;解决核心是绕过间隙锁,如用INSERT ON DUPLICATE KEY、降级到RC隔离级别或预占位。

在高并发环境下sql执行insert时如何防止死锁?

空表或低数据量时并发 INSERT 必然死锁,这不是配置或重试能解决的,而是 RR 隔离级别下间隙锁(Gap Lock)的固有行为。

为什么空表 INSERT 会死锁?

REPEATABLE READ 下,InnoDB 对 INSERT 使用 Next-Key Lock(记录锁 + 间隙锁)。空表时,主键范围是 (-∞, +∞),所有事务都试图在同一个间隙加 INSERT INTENTION LOCK——它本身兼容,但因锁排队机制,在无锚定行的情况下形成循环等待。InnoDB 检测后回滚其中一个事务,报错:Deadlock found when trying to get lock; try restarting transaction。

典型现象:两条并发插入,一条成功(id=1),另一条死锁后重试成功(id=3),id=2 跳号。

  • 这不是“运气差”,而是确定性行为:只要表为空、并发 INSERT、RR 级别,就必现
  • innodb_lock_wait_timeout 调大没用——问题不在等待超时,而在锁序冲突本身
  • 应用层重试只是掩盖症状,不减少锁竞争窗口

用 INSERT ... ON DUPLICATE KEY UPDATE 替代 SELECT + INSERT

很多业务写法是:SELECT ... FOR UPDATE 查是否存在 → 应用判断 → INSERT。这种模式极易死锁,尤其当 WHERE 字段没走唯一索引时,会升级为表锁或扩大间隙锁范围。

INSERT ... ON DUPLICATE KEY UPDATE 是原子操作,只锁命中行及其间隙,不触发额外的 SELECT 锁等待链:

Blogcast™
Blogcast™

Blogcast是一款把博客文章转换为自然语音音频的 AI 文本转语音工具。

下载
  • 必须确保字段有 UNIQUE KEY 或 PRIMARY KEY(如 order_no 建唯一索引)
  • 示例:INSERT INTO orders (order_no, user_id) VALUES ('ORDER123', 1001) ON DUPLICATE KEY UPDATE user_id = VALUES(user_id)
  • 注意:它仍会申请插入意向锁,但避免了“查→判→插”三步锁分离带来的环路风险

降级隔离级别到 READ COMMITTED

在确认业务可接受“不可重复读”的前提下,把事务隔离级别设为 READ COMMITTED,InnoDB 会禁用间隙锁和 Next-Key Lock,只保留记录锁:

  • 会话级执行:SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
  • 或全局配置:transaction_isolation = 'READ-COMMITTED'(写入 my.cnf)
  • 此时 INSERT 仅锁定实际插入的行,空表并发不再死锁
  • 代价是幻读可能重现:事务 A 查询无某订单,事务 B 插入并提交后,A 再查就看到——若该场景不影响核心逻辑(如订单创建后不二次校验),可接受

索引结构与锁序不一致才是隐藏杀手

死锁多数不是因为插同一行,而是多个二级索引导致加锁顺序不一致。例如表有 INDEX idx_status_uid (status, user_id) 和 INDEX idx_uid_created (user_id, created_at),一个事务按 status 扫描,另一个按 user_id 扫描,二者在不同索引树上加锁路径天然冲突。

  • 把高频共用字段(如 user_id)统一提到所有复合索引最左,例如改为 idx_uid_status 和 idx_uid_created
  • 删除低频、高重复值的单列索引(如只有 3–5 个值的 status 索引),它产生的 gap 锁范围大、竞争强
  • INSERT ... SELECT 比批量 VALUES 更危险:前者被当作逐行处理,无法合并加锁,且 ORDER BY 或 LIMIT 可能改变扫描顺序,放大锁序不一致概率

真正难处理的从来不是单点插入,而是多索引、多表、长事务交织下的锁序不可控——哪怕你用了 ON DUPLICATE KEY,只要索引设计没对齐,死锁依然会发生。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4603

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

951

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1209

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6581

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3323

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6540

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8981

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1210

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1072

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习