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 尚未进入插入阶段(未申请 XINSERT_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' 不存在
  • 申请 XINSERT_INTENTION
  • 执行插入并升级为 X
  • 插入成功,锁保持至提交或回滚

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

分析过程:

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

四、锁类型解析

锁类型 含义 作用
X 排他锁 禁止其他事务对相同索引记录加 XS
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_timeoutERROR 1205 日志 |

六、总结

  • InnoDB 唯一索引插入流程:

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

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

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

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

✍️ 结语:

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

相关推荐
qq_192779873 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
u0109272713 小时前
使用Plotly创建交互式图表
jvm·数据库·python
爱学习的阿磊3 小时前
Python GUI开发:Tkinter入门教程
jvm·数据库·python
tudficdew4 小时前
实战:用Python分析某电商销售数据
jvm·数据库·python
Fleshy数模4 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
sjjhd6524 小时前
Python日志记录(Logging)最佳实践
jvm·数据库·python
Configure-Handler4 小时前
buildroot System configuration
java·服务器·数据库
2301_821369614 小时前
用Python生成艺术:分形与算法绘图
jvm·数据库·python
az44yao5 小时前
mysql 创建事件 每天17点执行一个存储过程
mysql
电商API_180079052475 小时前
第三方淘宝商品详情 API 全维度调用指南:从技术对接到生产落地
java·大数据·前端·数据库·人工智能·网络爬虫