performance_schema.data_locks 能精确查看MySQL 8.0+中每条SQL所加的锁类型(RECORD/TABLE)、锁模式(X/REC_NOT_GAP、GAP等)、锁定的索引名、表名、具体记录值(LOCK_DATA)及关联事务ID(ENGINE_TRANSACTION_ID),是唯一可定位“哪条SQL、锁哪个索引、哪一行或间隙”的实时视图。

performance_schema.data_locks 能看到哪些锁细节
MySQL 8.0+ 中,performance_schema.data_locks 是监控行锁最直接的视图,它不只告诉你“有没有锁”,而是精确到锁类型、索引、记录值和事务ID。关键字段包括:LOCK_TYPE(值为 RECORD 才是行锁)、LOCK_MODE(如 X,REC_NOT_GAP 表示排他记录锁)、LOCK_DATA(被锁的具体索引值,例如 1005 或 'user_abc')、ENGINE_TRANSACTION_ID(关联到 INNODB_TRX 中的事务)。
容易忽略的一点:LOCK_DATA 在二级索引锁中可能显示主键值(回表后),不是原始 WHERE 条件值;若看到 NULL,大概率是隐式锁或未提交事务的预加锁。
如何区分表锁与行锁的开销来源
仅靠 data_locks 列表无法直接判断“开销”,需结合锁数量、持有时间、事务状态交叉验证:
- 查出所有
LOCK_TYPE = 'RECORD'的行数:如果单个事务持有了上万条RECORD锁,说明发生了锁升级(如全表扫描无索引)或大范围 UPDATE,这是高开销信号 - 对比
INFORMATION_SCHEMA.INNODB_TRX中的trx_started和当前时间:若trx_state = 'RUNNING'但已持续数分钟,且data_locks中对应大量RECORD锁,说明该事务长期占用行锁资源 - 检查
LOCK_MODE是否含GAP或NEXT-KEY:这类锁会阻塞插入,即使没更新数据,也可能拖慢写入吞吐
注意:LOCK_TYPE = 'TABLE' 出现在 DDL 或元数据操作中,与行锁无关;但若发现大量 TABLE 锁伴随 RECORD 锁,可能是 ALTER TABLE 正在等待行锁释放——此时真正瓶颈不在行锁本身,而在 DDL 阻塞。
为什么 data_lock_waits 比 show engine innodb status 更适合监控开销
SHOW ENGINE INNODB STATUS\G 的 TRANSACTIONS 部分只保留最近几秒的锁等待快照,且输出格式不便于程序解析;而 performance_schema.data_lock_waits 是实时、结构化、可 JOIN 的视图,能稳定捕获“谁在等谁”的完整链路:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
典型查询示例:
SELECT r.trx_id AS waiting_trx, r.trx_mysql_thread_id AS waiting_thread, w.BLOCKING_ENGINE_TRANSACTION_ID AS blocking_trx, l.LOCK_MODE, l.LOCK_DATA FROM performance_schema.data_lock_waits w JOIN performance_schema.data_locks l ON w.BLOCKING_ENGINE_LOCK_ID = l.ENGINE_LOCK_ID JOIN INFORMATION_SCHEMA.INNODB_TRX r ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID;
这个结果能直接暴露热点行(同一 LOCK_DATA 被多次等待)、锁竞争密度(一个 blocking_trx 被多个 waiting_trx 等待),比人工翻 INNODB STATUS 输出更可靠。
但要注意:data_lock_waits 只记录“正在等待”的关系,一旦等待超时或事务提交,记录即消失;所以做开销趋势分析时,必须配合定时采样(如每10秒抓一次快照存档),不能只查当前瞬时状态。
行锁开销高的三个典型误判场景
很多团队看到 data_locks 中锁数量多,就认定是“行锁太重”,其实常是诊断方向错了:
- 误把间隙锁当瓶颈:在
REPEATABLE READ下,SELECT ... FOR UPDATE范围查询会带GAP锁,但这些锁不阻塞读,只影响插入;若业务写入少、读多,实际开销极低 - 忽略事务粒度:一个长事务里执行了10次
UPDATE,每次锁1行,data_locks显示10条记录锁——这不是锁开销高,而是事务没及时提交 - 混淆锁与索引扫描成本:
data_locks显示锁了1000行,但根源可能是 WHERE 条件没走索引,导致 MySQL 扫描并锁定了1000行;此时优化重点是加索引,不是减少锁
真正需要警惕的行锁开销信号只有一个:多个活跃事务反复争抢同一行(相同 LOCK_DATA + 不同 trx_id),且伴随 INNODB_TRX 中 trx_weight 持续升高——这说明锁竞争已进入恶性循环,不是调参能解决的。

















