查innodb页级latch争用,首选show engine innodb status的semaphores段,重点关注os wait array info中spin waits与rounds比值、buf_pool_mutex或buf_block->lock等待;辅以performance_schema(需启用对应consumers/instruments)定位具体线程与内存地址,结合执行计划和业务模型优化。

查InnoDB页级Latch争用,先看SHOW ENGINE INNODB STATUS里的LATEST DETECTED DEADLOCK和SEMAPHORES段
InnoDB的页级Latch(如btr_search_latch、buf_pool_mutex、page_hash_lock)不是SQL层可见的锁,不走INFORMATION_SCHEMA.INNODB_TRX,得靠引擎内部状态。最直接的入口就是SHOW ENGINE INNODB STATUS——但注意:它只保留最近一次死锁和最近一段活跃的等待/信号量统计。
重点关注SEMAPHORES节里的OS WAIT ARRAY INFO部分:
– 如果某mutex或rw-lock的spin waits极高、rounds远大于spin waits,说明自旋失败多,开始频繁进系统等待,大概率是热点页争用;
– 若看到大量wait events集中在buf_pool_mutex或buf_block->lock,基本指向缓冲池页访问冲突。
- 执行前确保
innodb_status_output_locks = ON(MySQL 5.6.16+),否则TRANSACTIONS段不显示行锁/页锁细节 -
SHOW ENGINE INNODB STATUS输出是快照,需在业务高峰期间多次执行比对趋势,单次结果意义有限 - 别依赖
INFORMATION_SCHEMA.INNODB_METRICS里的innodb_buffer_pool_mutex_spin_waits等指标——它们是累加值,无时间粒度,难定位瞬时尖峰
用performance_schema定位具体SQL和页号(page_no)争用源
MySQL 5.7+ 的 performance_schema 能抓到更细粒度的Latch等待,关键是启用对应消费者和仪器:
- 开启:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_waits_current'; - 启用仪器:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'wait/synch/%/innodb%'; - 之后查
performance_schema.events_waits_current,过滤EVENT_NAME LIKE 'wait/synch/%/buf_pool%'或'wait/synch/%/btr_search%',能拿到THREAD_ID、SOURCE、OBJECT_INSTANCE_BEGIN(可反推页地址)
⚠️ 注意:OBJECT_INSTANCE_BEGIN 是内存地址,不是页号。若想关联到具体表空间+页号,需配合gdb或pstack在进程挂起时解析buf_block_t结构体,或改用Percona Server的innodb_metrics扩展字段(如innodb_buffer_pool_pages_data + page_get_page_no()调用栈)。
innodb_adaptive_hash_index = OFF可能缓解btr_search_latch争用,但得看场景
btr_search_latch是B+树自适应哈希索引的全局读写锁,高并发点查(尤其主键等值查询)易成瓶颈。关掉AHK确实能消除这个Latch,但代价是失去哈希查找的O(1)优势,回归B+树的O(log n)遍历。
- 适用场景:业务以范围扫描、JOIN为主,或主键查询已命中足够多的缓冲池页(
buf_pool_hit_rate > 99%),此时AHK收益低,争用反而明显 - 不适用场景:大量短小主键等值查询(如用户中心ID查)、且QPS > 5k,关AHK后CPU和buffer pool scan压力会陡增
- 验证方法:压测前后对比
SHOW ENGINE INNODB STATUS里btr_search_latch的spin waits和os_waits变化,同时观察Handler_read_key/Handler_read_next比例
批量更新/删除导致buf_block->lock争用?优先检查是否锁了不该锁的页
大事务批量操作(如DELETE FROM t WHERE ts )会逐页加载、加<code>buf_block->lock(页级rw-lock),若没走索引或索引区分度差,容易扫到大量无关页,把Latch争用扩散到整个buffer pool。
- 确认执行计划:
EXPLAIN FORMAT=JSON看是否用了覆盖索引、是否rows_examined远超实际影响行数 - 避免全表扫描式批量删:改用按主键分段(
WHERE id BETWEEN ? AND ?),每次控制在1k行内,减少单次持有的页锁数量和时间 - 检查
innodb_change_buffering设置:对非唯一二级索引的DML,开启change buffer可延迟页加载,间接降低buf_block->lock争用,但仅适用于非唯一索引+非查询场景
真正棘手的是多个线程反复访问同一组热页(比如计数器表、配置缓存页),这时Latch争用本质是业务模型问题,不是参数能调平的——得考虑分片、本地缓存或异步化。










