explain 只显示预估执行计划,不实际执行,无法反映磁盘i/o、锁等待、mvcc开销等真实耗时;需结合performance_schema、慢日志分析及sys库视图定位瓶颈。
为什么 explain 看不出真实执行耗时
因为 explain 只展示查询计划,不实际执行,更不会统计磁盘 i/o 或锁等待。你看到的 type=ref 和 rows=100 是预估,不是实测结果。
真正卡顿往往发生在:大字段读取、临时表落盘、排序溢出内存、或被其他事务阻塞。这时候光看 EXPLAIN 会误判为“索引已用,应该很快”。
- 必须配合
SELECT ... INTO DUMPFILE或SHOW PROFILE(MySQL 5.7+ 已弃用,改用performance_schema)验证实际阶段耗时 - 在慢查询日志中开启
log_queries_not_using_indexes=ON,但注意它不捕获已用索引却仍慢的语句 - 对
ORDER BY+LIMIT类查询,EXPLAIN的rows常严重低估——因为优化器按索引顺序扫描,但实际要跳过大量已删行(MVCC 可见性判断开销没计入)
如何用 sys.schema_table_statistics_with_buffer 快速定位热点表
这个视图是 MySQL 5.7+ sys 库里最实用的性能快照之一,它把 information_schema.TABLE_STATISTICS 和缓冲池访问合并,直接暴露“哪些表既读得多、又常被刷出 buffer pool”。
别手动算 innodb_buffer_pool_read_requests / innodb_buffer_pool_reads,那个比值掩盖了单表不均衡问题。
- 查前 5 个物理读最多的表:
SELECT * FROM sys.schema_table_statistics_with_buffer ORDER BY ios_by_read DESC LIMIT 5;
- 如果某表
buffer_pool_pages_dirty > 0且ios_by_read很高,说明它频繁修改又频繁重读——极可能是缓存未命中导致的恶性循环 - 注意该视图默认只统计 InnoDB 表;MyISAM 表需单独查
information_schema.TABLES的DATA_LENGTH和INDEX_LENGTH
pt-query-digest 解析慢日志时为什么漏掉 Prepared Statement
MySQL 默认慢日志记录的是原始 SQL 文本,而 Prepared Statement 在日志里显示为 Prepare 和 Execute 两条独立事件,pt-query-digest 默认只聚合 Execute,但参数绑定后的实际执行计划可能和 Prepare 阶段完全不同。
更麻烦的是:如果应用用 mysql_stmt_execute() 并启用了 query_cache_type=DEMAND,慢日志甚至可能完全不记录该语句。
- 解析前先用
mysqldumpslow -s t快筛,确认是否存在大量Prepare后跟不同Execute的模式 - 运行
pt-query-digest --filter '$event->{fingerprint} =~ m/^\s*execute/i' slow.log强制只分析 execute 行 - 在 MySQL 8.0+ 中,优先开启
performance_schema.events_statements_history_long,用sys.statement_analysis替代日志解析,它能还原绑定参数后的完整指纹
可视化分析时,innodb_buffer_pool_pages_data 突降不等于内存不足
这个状态变量下降,常见误解是“内存被占满,MySQL 在疯狂刷脏页”。实际上,它更可能反映:表被 DROP、TRUNCATE,或 Buffer Pool 被在线调整大小(SET GLOBAL innodb_buffer_pool_size)。
尤其在 K8s 环境下,如果 Pod 内存 limit 设置过紧,Linux OOM Killer 杀掉 mysqld 后重启,pages_data 会归零——但这和 Buffer Pool 配置无关,而是进程级崩溃。
- 先查
SHOW ENGINE INNODB STATUS\G里的BUF POOL小节,对比Database pages和Free buffers是否同步下降 - 检查
information_schema.INNODB_METRICS中buffer_pool_resize_status是否为 1(表示正在 resize) - 用
grep -i "out of memory" /var/log/syslog确认是否发生 OOM,而不是只盯着 MySQL 内部指标
真正的 Buffer Pool 压力,要看 innodb_buffer_pool_wait_free 是否持续增长,以及 innodb_buffer_pool_read_ahead_evicted 是否突增——后者说明预读页刚加载就被踢出,是典型的缓存抖动信号。











