开放问答
联合索引 type=ref 单次 3ms,200 QPS 并发下 P99 飙到 150ms:如何系统定位是回表随机 IO 还是其他?
该问题的正文、回答与评论。
提问者 陈墨白 · 状态 open
MySQL慢查询并发性能InnoDB回表
【背景】 我们是一套 MySQL 8.0.32 / InnoDB 的订单查询服务,核心表 t_order 约 5200 万行、单表 9.8GB(含二级索引)。高频查询按 (merchant_id, status, create_time) 联合索引取最近 50 条订单,EXPLAIN 结果 type=ref、key=idx_merchant_status_ct、rows≈1800、Extra 为空(无 Using filesort / Using temporary)。单次执行耗时稳定在 2~5ms,buffer pool 大小 16GB(机器 64GB),hit ratio 日常 99.5%+。
【现象】 压测到 200 QPS 持续 10 分钟时,P50 基本不变(6ms),但 P99 从 8ms 一路爬到 120~180ms,P999 超过 400ms。连接池 HikariCP max=50,业务侧 JDK 17。监控上能看到:
- Innodb_buffer_pool_reads(物理读)在压测期间持续上涨,但绝对值不大(每分钟几千次),不像冷启动那种暴涨;
- Innodb_row_lock_waits 几乎不动,SHOW ENGINE INNODB STATUS 的 TRANSACTIONS 段没有明显锁等待;
- 系统层 iostat 显示磁盘 util 从 5% 涨到 35%,await 从 0.1ms 涨到 2.8ms,但远没打满;
- 慢日志(long_query_time=0.05)里抓到的慢查询全是同一条 SQL,耗时集中在 80~300ms 区间。
【已尝试的排查】
- 确认不是连接池耗尽:HikariCP pending 始终为 0;
- 确认不是锁:Innodb_row_lock_waits 增量 < 10/min,没有死锁记录;
- 确认不是 filesort / temporary:EXPLAIN Extra 干净,performance_schema 里 sort 相关计数器无增长;
- 把 buffer pool 临时调到 32GB 重跑,P99 有改善(150→90ms)但没有根治;
- 怀疑是二级索引 → 主键回表的随机 IO 在并发下放大(5200 万行,回表 50 次 ≈ 50 次 16KB 随机读),但拿不出一个可量化的证据链。
【期望得到的答案形态】
- 有没有一套从监控指标 → 定位到具体是回表 IO / buffer pool 逐出 / 还是其他的系统性诊断路径?(比如用 performance_schema 的 table_io_waits_summary_by_index_usage 怎么读、Innodb_buffer_pool_read_ahead 系列指标怎么配合判断)
- 如果确认是回表随机 IO,除了「把 buffer pool 加到装下热数据」之外,有没有更结构化的解法(覆盖索引、按 merchant_id 分区、把 create_time 纳入索引让回表次数降到 0)?各自的代价和适用边界是什么?
- 压测时怎么把「单次 3ms 的查询在 200 并发下 P99 飙高」这个现象稳定复现并量化归因,而不是靠猜?
希望有实战经验的朋友给一个可落地的排查 checklist,最好带具体的 performance_schema 查询语句和阈值判断。
评论
暂无评论。