为什么MySQL在唯一索引碰撞时会加S锁_分析二级索引加锁逻辑

风芳大大_2557

风芳大大_2557

2026-05-15

1021人浏览

原创

唯一索引冲突时加s锁而非x锁,因冲突本质是插入失败而非修改数据,s锁允许多个事务并发读但阻止删改,兼顾一致性与响应速度;二级唯一索引加s next-key锁(非纯记录锁),为防幻读级覆盖,即使rc隔离级别也不退化。

为什么mysql在唯一索引碰撞时会加s锁_分析二级索引加锁逻辑

唯一索引冲突时为什么加 S 锁而不是 X 锁

因为冲突本身不是“要改老数据”,而是“新插入失败”,加 X 锁会无谓阻塞读——业务上往往需要立刻感知冲突(比如提示用户“该手机号已注册”),同时还要能查到原记录做校验或展示。S 锁允许并发读,但阻止其他事务删/改那条冲突记录,刚好守住一致性底线。

常见错误现象:INSERT IGNORE 或 REPLACE INTO 在高并发下突然卡住,SHOW ENGINE INNODB STATUS\G 显示大量 lock_mode S waiting ——这不是锁错了,是 S 锁正在排队等释放。

注意:这个 S 锁是加在**唯一二级索引记录上**,不是主键;且只在事务中发生(即显式 BEGIN 后的 INSERT),自动提交模式下锁会立刻释放,不易观察。

为什么二级唯一索引加的是 S Next-key 锁,不是纯记录锁

Next-key 锁 = 记录锁 + 间隙锁,左开右闭。对唯一二级索引加 Next-key 锁,核心是为了防止“幻读级覆盖”:比如 name='alice' 已存在,若只加记录锁,另一个事务仍可能在相同 name 值附近插入新行(虽然值不同,但索引排序位置紧邻),干扰唯一性语义的边界判断。

使用场景典型如秒杀名额、邀请码绑定——你不能只防住“完全相同的值”,还要防住“在它前后插入导致逻辑错乱”的情况。

关键差异点:

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载
  • PRIMARY KEY 冲突 → 加 S Record Lock(仅锁那一条)
  • UNIQUE KEY(非主键)冲突 → 加 S Next-key Lock(锁记录 + 它左边的间隙)
  • 即使隔离级别是 READ COMMITTED,唯一索引冲突仍会加 Next-key 锁(MySQL 文档明确说明,不因 RC 而退化)

死锁常出现在 S 锁与 X 锁交叉等待

最典型的死锁组合:INSERT IGNORE(申请 S Next-key 锁)和 UPDATE ... WHERE unique_col = ?(申请 X Next-key 锁)并发执行。两者都先拿到 S 锁,再争抢 X 锁,形成循环等待。

示例复现步骤:

  • 事务 A:INSERT IGNORE INTO t1 (name, level) VALUES ('A', 0); → 成功获得 uk_name 上的 S Next-key 锁
  • 事务 B:INSERT IGNORE INTO t1 (name, level) VALUES ('A', 0); → 同样获得 S Next-key 锁(S-S 兼容)
  • 事务 A:UPDATE t1 SET level = 1 WHERE name = 'A'; → 等待 X 锁,被 B 的 S 锁阻塞
  • 事务 B:UPDATE t1 SET level = 2 WHERE name = 'A'; → 同样等待 X 锁,但此时 A 持有 S 锁未释放,B 也持 S 锁,InnoDB 判定死锁

这种模式在重试逻辑里特别危险:消息队列重复投递 + INSERT IGNORE + 后续状态更新,三者一凑齐就容易触发。

排查和缓解的关键点

真正容易被忽略的不是“怎么加锁”,而是“锁什么时候释放”。S Next-key 锁在事务结束(COMMIT 或 ROLLBACK)前一直持有,哪怕 INSERT 早已报错返回。

实操建议:

  • 用 SELECT * FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'db' AND OBJECT_NAME = 't1'\G 查实时锁,注意看 LOCK_MODE 和 LOCK_DATA
  • 避免在长事务里混用 INSERT IGNORE 和基于同一唯一索引的 UPDATE
  • 若业务允许,把唯一约束检查前置到应用层(如先 SELECT 再决定是否 INSERT),但要注意 SELECT ... FOR UPDATE 会加 X 锁,代价更高
  • 写重试逻辑时,不要无脑重试 INSERT IGNORE,应捕获 ERROR 1062 (23000) 后直接跳过或走更新分支

复杂点在于:Next-key 锁的“间隙”部分不可见、不可控,你看到的 LOCK_DATA 只显示记录值,但实际封锁范围得靠索引顺序推算——这正是线上死锁难以复现又难以根治的原因。

相关专题

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

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

2023.10.12

4443

8

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

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

2023.10.27

931

4

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

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

2024.02.23

1169

5

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

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

2024.03.06

6381

10

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

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

2024.03.06

3183

4

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

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

2024.04.07

6340

11

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

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

2024.04.29

8661

6

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

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

2024.04.29

1170

5

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

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

2024.04.29

1032

5

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 189人学习