MySQL 8.0.26 RR隔离级别下主键supremum伪记录锁疑问
唯一键冲突后主键supremum伪记录锁的成因分析
环境与前提
- MySQL版本:8.0.26
- 事务隔离级别:REPEATABLE-READ(可重复读)
表结构
create table test ( id int auto_increment primary key, a varchar(256) null, b int null, c int null, create_time timestamp default CURRENT_TIMESTAMP null, constraint uni_key unique (a, b, c) );
初始化数据
insert into test(a,b,c) values ('a',1,1);
会话操作流程
| SessionA | SessionB |
|---|---|
| begin; | begin; |
| insert into test(a,b,c) values ('a',1,1); | |
| ERROR 1062 (23000): Duplicate entry | insert into test(a,b,c) values ('b',2,3); |
| (blocked) |
Session B会被阻塞直到Session A提交。
锁信息查询结果
执行查询语句:
SELECT ENGINE_LOCK_ID as Lock_Id, ENGINE_TRANSACTION_ID as Trx_id, OBJECT_NAME as `Table`, INDEX_NAME as `Index`, LOCK_DATA as Data, LOCK_MODE as Mode, LOCK_STATUS as Status, LOCK_TYPE as Type FROM performance_schema.data_locks;
返回结果:
+-----------------------------------------+--------+-------+---------+------------------------+------+---------+--------+ | Lock_Id | Trx_id | Table | Index | Data | Mode | Status | Type | +-----------------------------------------+--------+-------+---------+------------------------+------+---------+--------+ | 140135522398208:1088:140135454819312 | 15656 | test | NULL | NULL | IX | GRANTED | TABLE | | 140135522398208:23:5:27:140135454816400 | 15656 | test | uni_key | 'a', 1, 1, 64 | S | GRANTED | RECORD | | 140135522398208:23:4:1:140135454817088 | 15656 | test | PRIMARY | supremum pseudo-record | X | GRANTED | RECORD | +-----------------------------------------+--------+-------+---------+------------------------+------+---------+--------+ 3 rows in set (0.01 sec)
问题解答
1. 唯一键冲突后主键supremum伪记录锁的成因
当SessionA执行插入触发唯一键冲突时,InnoDB的处理逻辑如下:
- 首先在冲突的唯一键
uni_key上加S锁,防止其他事务修改这条冲突记录,避免数据不一致或幻读; - 其次,InnoDB内部会先尝试为新插入行分配自增主键(即使插入最终失败)。由于自增主键必然大于现有所有主键值,会定位到主键索引的supremum伪记录(InnoDB为索引末尾虚拟的边界记录),并给这个伪记录加X锁。目的是阻塞其他事务插入新的自增主键行,保证可重复读隔离级别下的事务一致性视图,避免其他事务的插入操作破坏当前事务的冲突状态判断。
2. 无冲突时SessionB可执行的原因
当SessionA插入无唯一键冲突时:
- 成功分配自增主键并将数据插入主键索引和唯一键索引;
- 事务未提交时仅持有自身插入记录的锁,SessionB插入的记录在主键和唯一键上的位置与SessionA的记录无重叠,无需获取被持有的锁,因此可以正常执行。
3. 唯一键错误的处理逻辑
InnoDB检测到唯一键冲突时:
- 先完成冲突检测,确认唯一键已存在;
- 不会立即释放已持有的锁(包括唯一键的S锁和主键的supremum X锁),因为当前事务仍处于未提交/未回滚状态;
- 抛出1062错误,但事务不会自动回滚(默认参数下),直到SessionA主动提交或回滚,锁才会释放,SessionB才能继续执行。
这种锁的持有逻辑是为了保证事务的原子性和隔离性,避免事务未结束前其他事务修改相关数据导致冲突状态被破坏。
内容的提问来源于stack exchange,提问作者supergaga
相关产品推荐
相关产品推荐

