查被锁最狠的表应先用sys.innodb_lock_waits定位锁等待,再关联x$innodb_lock_waits或INNODB_TRX/INNODB_LOCKS(5.7)/data_locks(8.0+)获取locked_table;注意权限、consumer启用及版本兼容性。

查哪些表被锁得最狠:用 sys.innodb_lock_waits 定位热点
直接看 sys.innodb_lock_waits 是最快的方式,它把 InnoDB 的锁等待关系做了聚合,但注意——这张视图底层依赖 performance_schema,如果没开对应消费者,会返回空。默认 MySQL 8.0+ 开了大部分,5.7 需手动确认:UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events%lock%';
常见错误是只查 sys.innodb_lock_waits 就停了,其实它不带表名;真正要找“哪张表争用最多”,得连查 sys.innodb_lock_waits → sys.x$innodb_lock_waits(含 locked_table 字段)→ 或更稳一点:关联 information_schema.INNODB_TRX 和 INNODB_LOCKS(MySQL 5.7)或 performance_schema.data_locks(8.0+)。
- 5.7 环境优先用:
SELECT trx_mysql_thread_id, trx_query, trx_state, trx_wait_started, il.lock_trx_id, il.lock_table FROM information_schema.INNODB_TRX trx JOIN information_schema.INNODB_LOCK_WAITS ilw ON trx.trx_id = ilw.requesting_trx_id JOIN information_schema.INNODB_LOCKS il ON ilw.blocking_lock_id = il.lock_id; - 8.0+ 推荐走
performance_schema.data_locks+data_lock_waits,字段更全、无幻读风险 -
lock_table字段格式是`db_name`.`table_name`,注意反引号,解析时别直接当字符串切分
为什么 sys.schema_table_statistics_with_buffer 比 SHOW OPEN TABLES 更准
SHOW OPEN TABLES 只反映当前被打开的表句柄数,和锁争用几乎无关;而 sys.schema_table_statistics_with_buffer 里 rows_affected、rows_read、fetch_latency 这些指标,结合 io_latency 能看出某张表是否长期卡在 I/O 或锁上。尤其当 fetch_latency 高但 io_latency 低,大概率是行锁/间隙锁阻塞了读操作。
- 查热点表时,排序别只看
rows_read,加权看:fetch_latency / rows_read(单次读延迟)和io_latency / rows_fetched(I/O 效率) - 该视图默认不统计临时表,如果业务大量用
CREATE TEMPORARY TABLE,这部分锁不会出现在结果里 - 字段里的
buffer_pool_pages_dirty如果持续 > 0,说明该表脏页多、刷盘压力大,可能加剧锁等待(尤其是长事务未提交时)
sys.innodb_lock_waits 返回空?检查这三件事
不是数据没锁,而是诊断链断了。最常踩的坑是权限、配置、版本混用。
- 用户必须有
SELECT权限在performance_schema库下,光有PROCESS不够 - MySQL 5.7 默认关了
performance_schema的锁相关 consumer:events_transactions_history_long和data_locks必须设为ENABLED - MySQL 8.0.3 之后,
sys.innodb_lock_waits视图被重写,依赖performance_schema.data_lock_waits;若升级后没重建 sys 库(mysql_upgrade没跑),视图结构错位,可能字段为空或报错Unknown column 'blocking_trx_id' in 'field list'
别只盯着表名:锁类型和索引才是根因
看到 locked_table = `orders`.`order_items` 只是起点。同一张表,INSERT ... SELECT 和 UPDATE ... WHERE status = ? 引发的锁粒度、持有时间、冲突模式完全不同。真正要调优,得进 performance_schema.data_locks 看 LOCK_MODE(S, X, IS, IX, GAP, REC_NOT_GAP)和 LOCK_DATA(具体锁住的索引值)。
- 如果
LOCK_MODE大量是X, GAP,说明有范围更新或唯一索引缺失,导致间隙锁膨胀 -
LOCK_DATA显示NULL?那很可能是表级锁(如ALTER TABLE、FLUSH TABLES WITH READ LOCK),不是行锁问题 - 没有合适索引的
WHERE条件,会让 UPDATE/DELETE 升级为全表扫描+全表加锁,这时sys.schema_table_statistics_with_buffer的rows_examined会异常高
锁争用从来不在表名上,而在语句怎么写、索引有没有覆盖、事务是不是太长。盯住 LOCK_DATA 和执行计划,比数“哪个表被锁最多”有用得多。



















