MySQL InnoDB 行锁与死锁排查实战演示

MySQL InnoDB 行锁与死锁排查实战演示

💡 导语 :

在高并发环境下,MySQL InnoDB 存储引擎的行锁与唯一索引机制经常成为性能瓶颈。

本文通过一个可复现的示例,系统演示如何分析 事务锁等待 与 死锁问题 ,并利用 performance_schema.data_locks 精确定位锁冲突来源。

阅读本文,你将掌握:

  • InnoDB 行锁、间隙锁、插入意向锁的原理
  • 死锁形成过程及排查思路
  • performance_schema 的实战使用方法

目录


零:环境信息

mysql版本:8.0.32

innodb引擎

一、建表 SQL

sql 复制代码
CREATE DATABASE IF NOT EXISTS demo;
USE demo;
DROP TABLE IF EXISTS orders;

CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_no VARCHAR(20) NOT NULL UNIQUE,
    user_id BIGINT NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

说明:

  • order_no 为唯一索引字段,用于演示唯一约束下的锁行为
  • 使用 InnoDB 引擎
  • 默认事务隔离级别为 REPEATABLE READ

二、事务演示场景

我们模拟三个事务 A、B、C,均尝试插入同一个唯一索引值 'A10004':

事务 操作 状态
A 开启事务并插入 'A10004',不提交 持有锁
B 开启事务插入 'A10004' 阻塞等待 A
C 开启事务插入 'A10004' 阻塞等待 B
A 回滚事务 唤醒 B
B 插入成功后提交 C 被检测为死锁报错

三、执行流程与锁分析

1. 事务 A 插入

BEGIN;

INSERT INTO orders(order_no, user_id, amount) VALUES ('A10004', 1, 100.00);

锁行为:

  • 对唯一索引 'A10004' 加 X(排他)锁
  • 对表加 IX(意向排他)锁
  • 状态:锁获取成功,但事务未提交

2. 事务 B 插入

BEGIN;

INSERT INTO orders(order_no, user_id, amount) VALUES ('A10004', 2, 100.00);

锁行为:

  • 尝试加 S(共享)锁检查 'A10004' 是否存在
  • 'A10004' 被事务 A 持有 X 锁,B 阻塞
  • 此时 B 尚未进入插入阶段(未申请 X、INSERT_INTENTION 锁)

3. 事务 C 插入

BEGIN;

INSERT INTO orders(order_no, user_id, amount) VALUES ('A10004', 3, 100.00);

锁行为:

  • 同样尝试加 S 锁检查唯一性
  • 'A10004' 被 A 阻塞
  • 队列等待顺序:B → C

4. 事务 A 回滚

ROLLBACK;

行为:

  • 释放 'A10004' 上的 X 锁
  • 唤醒等待队列
  • B 被唤醒执行,C 继续等待

5. 事务 B 执行插入

执行步骤:

  • 唤醒后加 S 锁检查唯一性
  • 发现 'A10004' 不存在
  • 申请 X、INSERT_INTENTION 锁
  • 执行插入并升级为 X 锁
  • 插入成功,锁保持至提交或回滚

6. 事务 C 唤醒并触发死锁

分析过程:

  • 唤醒后尝试加 S 锁检查唯一性
  • 'A10004' 已被 B 插入并持有 X、INSERT_INTENTION 锁
  • C 需要等待 B 的锁,形成循环等待
  • InnoDB 死锁检测机制触发,C 被回滚
    报错信息:
    ERROR 1213 (Deadlock): Deadlock found when trying to get lock; try restarting transaction

四、锁类型解析

锁类型 含义 作用
X 排他锁 禁止其他事务对相同索引记录加 X 或 S 锁
S 共享锁 允许其他事务加 S 锁,但阻止 X 锁
X, INSERT_INTENTION 插入意向锁 对间隙加锁,保证插入唯一值时的顺序性
S, GAP 共享间隙锁 锁定索引间隙,防止其他事务插入相同值,但允许读取

五、排查技巧

  1. 查看当前事务
    SELECT * FROM information_schema.innodb_trx;
  2. 查看锁信息
    SELECT * FROM performance_schema.data_locks\G;
    关键字段说明:
    | 字段 | 含义 |
    |---|---|
    | LOCK_MODE | 锁类型(X / S / GAP 等) |
    | LOCK_STATUS | 锁状态:GRANTED(已获锁)或 WAITING(等待中) |
    | OBJECT_INSTANCE_BEGIN | 锁对象地址,可用于定位索引记录 |
    | THREAD_ID | 对应会话线程 ID |
  3. 常见问题判断
    | 现象 | 排查思路 |
    |---|---|
    | 锁等待 | 查看 LOCK_STATUS=WAITING 的记录 |
    | 死锁 | 查看 SHOW ENGINE INNODB STATUS 或错误日志中的 ERROR 1213 |
    | 超时 | 检查 innodb_lock_wait_timeout 和 ERROR 1205 日志 |

六、总结

  • InnoDB 唯一索引插入流程:

    • 加 S 锁检查唯一性
    • 申请 X、INSERT_INTENTION 锁
    • 最终升级为 X 锁
  • 锁等待顺序:

    • InnoDB 遵循 FIFO 队列
    • 前一个事务释放锁后,等待事务依次被唤醒
  • 死锁检测机制:

    • 当多个事务互相等待彼此持有的锁时,InnoDB 会主动检测并回滚其中一个事务以打破死锁
  • 实践建议:

    • 使用 performance_schema.data_locks 精确分析锁类型与等待对象
    • 避免长事务或高并发下重复插入相同唯一键
    • 通过减少唯一索引竞争、控制事务粒度来降低死锁风险

✍️ 结语:

死锁是并发系统中无法完全避免的现象,但通过合理的事务控制与锁分析手段,可以让我们快速定位并解决问题。 如果你在实际项目中遇到复杂锁等待场景,不妨打开 performance_schema.data_locks 查看锁信息,很多答案都在那儿。

相关推荐
可乐鸡翅yeah_4 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
石头麻辣鱼5 小时前
Bamboo 调度系统 OceanBase 适配实战:存储过程迁移踩过的三个大坑
数据库
龙亘川5 小时前
数智驱动民政升级 精准守护民生保障
大数据·数据库·人工智能
念何架构之路5 小时前
zap扩展生态与总结
java·前端·数据库
晚安日记wanna6 小时前
MySQL 回表为什么这么慢?5 种优化手段逐个拆解
数据库·mysql·性能优化
第七页独白6 小时前
汽车零件厂如何通过 QMS 真正落地 IATF 16949——QMS软件系统:品质检验-内审稽核-8d客诉管理:全星质量管理软件系统
java·前端·数据库
晚安日记wanna6 小时前
索引建了却不走?7 个失效原因逐层排查
数据库·mysql·性能优化
KID1412号6 小时前
PostgreSQL入门
数据库·postgresql
Elastic 中国社区官方博客6 小时前
使用 Lucene 搜索你的 Bean — 索引
java·大数据·数据库·elasticsearch·搜索引擎·全文检索·lucene
ShineWinsu8 小时前
对于Redis:主从复制的解析
linux·数据库·c++·redis·缓存·面试·主从复制