别再盯着EXPLAIN的rows列了,8.0.18之后有更好的选择

简介: EXPLAIN是DBA最常用的工具之一,但大多数人还在看type、rows、Extra这些传统字段——然后靠经验猜。MySQL 8.0.18开始引入的EXPLAIN ANALYZE,直接把实际执行时间和行数输出给你看,不用猜了。本文对比传统EXPLAIN和EXPLAIN ANALYZE的差异,展示如何用新工具把执行计划分析这件事从“猜”变成“看”。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

前两天一个同事跑过来问我:“小耶,你看这个执行计划,type是ref,rows是1000,Extra里用了索引,为啥还是慢?”

我看了眼SQL,又看了眼他的执行计划,问了一句:“你用的是EXPLAIN还是EXPLAIN ANALYZE?”

他说:“EXPLAIN啊,用了十几年了。”

——问题就出在这儿。

EXPLAIN给你看的是“优化器的预测”,不是“实际执行的结果” 。优化器说rows=1000,实际可能扫了100万行;优化器说用索引,实际可能回表回了10万次。

MySQL 8.0.18开始引入的EXPLAIN ANALYZE,直接把实际执行时间和实际行数输出给你看,把执行计划分析这件事从“猜”变成了“看”。

一、传统EXPLAIN的局限

传统的EXPLAIN输出,你看到的是:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE orders ref idx_user_id idx_user_id 4 const 1000 Using index condition

rows=1000 —— 这是优化器估算的需要扫描的行数,不是实际扫描的行数。

如果统计信息过期、数据分布倾斜、或者优化器的基数估算出了偏差,这个数字可能和实际情况差一个数量级。

你基于一个错误的估算去优化,方向可能从一开始就偏了。

二、EXPLAIN ANALYZE带来了什么

EXPLAIN ANALYZE实际上会执行SQL(是的,它会真的跑一遍),然后输出每个执行步骤的实际执行统计信息:

EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 12345 AND create_time > '2026-01-01';

输出示例(MySQL 8.0+):

-> Filter: (orders.create_time > '2026-01-01')  (cost=1234 rows=1000) (actual time=0.5..45.2 rows=856 loops=1)
    -> Index lookup on orders using idx_user_id (user_id=12345)  (cost=1234 rows=1000) (actual time=0.4..42.1 rows=12345 loops=1)

关键差异:

传统EXPLAIN EXPLAIN ANALYZE
估算行数(rows) 实际行数(actual rows)
估算成本(cost) 实际执行时间(actual time)
只看计划 看到每个步骤的真实开销

在上面这个例子里,优化器估算rows=1000,实际扫描了rows=12345——差了12倍。如果你只看传统EXPLAIN,可能觉得“只扫1000行,没问题”,但实际上回表回了1万多行,慢是必然的。

三、怎么用EXPLAIN ANALYZE定位问题

第一步:找到最慢的那个步骤

actual time告诉你每个步骤实际花了多少毫秒。哪一步时间最长,哪一步就是瓶颈。

第二步:对比估算值和实际值

如果rows和actual rows差距很大(比如差10倍以上),说明统计信息可能过期了——先更新统计信息,再看看执行计划有没有变化。

第三步:看loops

loops表示这个步骤被执行了多少次。如果loops很大(比如>1000),说明有大量的重复执行——可能是嵌套循环连接(Nested Loop Join)的驱动表选错了,或者子查询被反复执行。

四、注意事项

  • EXPLAIN ANALYZE会实际执行SQL,所以不要在写操作上直接跑——先在测试环境或只读从库上验证

  • 对于耗时很长的SQL,EXPLAIN ANALYZE本身也要跑那么久,要有耐心

  • MySQL 5.7及以下版本不支持,需要升级到8.0.18+

  • EXPLAIN ANALYZE的输出比传统EXPLAIN详细得多,需要花时间熟悉格式

五、小结

EXPLAIN用了十几年,是时候升级到EXPLAIN ANALYZE了。前者给你看“优化器的预测”,后者给你看“实际执行的结果”。从“猜”到“看”,这是执行计划分析这件事上最大的认知升级。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
1月前
|
SQL 监控 关系型数据库
MySQL索引合并优化器陷阱:为什么复合索引比索引合并快一个数量级?
MySQL优化器有一个“自作聪明”的行为——当单列索引无法完全覆盖查询时,它可能选择索引合并(Index Merge) ,同时使用多个单列索引,把结果集合并起来。听起来很合理对吧?但索引合并有严格的适用条件,用错了比全表扫描还慢——尤其是UNION类型的索引合并,需要对多个结果集去重和排序,代价极高。本文拆解索引合并的3种类型、3个踩坑场景,以及什么时候该用复合索引替代。
|
1月前
|
存储 SQL 容灾
共享存储集群 vs 分布式多副本:同城双活两条技术路线怎么选?
同城双活正在成为金融、政务等核心系统的容灾标配——RPO=0、RTO<30秒。但真正的落地远不止“两个机房各放一套数据库”那么简单。网络延迟的容忍度、脑裂预防机制、同步复制的性能代价、以及故障切换后的数据回滚,每一个环节都可能成为“最后一公里”的绊脚石。本文从容灾架构演进入手,拆解同城双活的核心技术原理、关键挑战与应对方案,并结合同城双中心方案及实测数据进行深度解析。
|
1月前
|
SQL 存储 关系型数据库
分区裁剪失效、DDL卡死、元数据爆炸:分区表的3个真实代价
很多人觉得分区表是“轻量级分库分表”——数据分开放、查询只扫一个区、过期数据直接DROP分区,听起来很完美。但分区表有严格的适用边界和隐藏代价:分区键选错导致所有查询都扫全部分区、分区数量过多导致DDL巨慢、跨分区查询比普通表还慢……本文从分区表的核心原理出发,拆解4种分区类型、3个真实踩坑场景,以及分区表与分库分表的本质区别,帮你一次性搞清楚到底该不该用。
|
1月前
|
SQL 人工智能 运维
3个月AI Agent运维实测:慢SQL它管,根因还得我上
以三个月实测的视角,划清AI Agent自治运维的真实能力边界:巡检、慢SQL发现等重复活已可替代,复杂根因、变更审批、数据兜底仍需人把关,探讨DBA角色从救火队员向定规则、把关人的转型。
|
1月前
|
SQL 人工智能 数据库
AI写的SQL语法对、性能炸?上线前五道关卡能救命
从AI生成SQL的三大翻车模式(字段幻觉、性能灾难、语义错误)出发,给出上线前五道审核关卡:结构预检、执行计划校验、高危操作拦截、灰度上线、审计追踪,附SQL示例与避坑清单。
|
1月前
|
安全 关系型数据库 MySQL
高并发下1档只慢一点?innodb_flush_log_at_trx_commit的0/1/2实测
生成图片:不要沿用上面的图片风格,重新生成 4 张文章封面图供我选择,16:9 主标题:MySQL持久性最佳实践 副标题:redo刷盘参数三档取舍与故障分析 文章概述:实测innodb_flush_log_at_trx_commit的0、1、2三档性能,讲清进程崩溃与断电下的丢数据边界,以及redo、doublewrite、双1的关系,给出选型建议。
|
1月前
|
SQL 运维 关系型数据库
2026年分布式数据库有哪些主流选择?先评估这5个维度再决定
分布式数据库选型,99%的文章在列表格比参数——但真正的决策关键不在厂商PPT里,在上线后的真实运维里。本文从网络延迟容忍度、SQL兼容性验证、在线扩容能力、全局索引代价、运维工具链成熟度5个维度出发,给出可落地的评估方法和决策建议,帮助你在选型阶段避开那些“只有上线后才会发现”的坑。
|
1月前
|
SQL 关系型数据库 MySQL
死锁报错看了三遍没看懂?我拆给你看(附定位SQL)
从一次真实死锁现场切入,讲清行锁、间隙锁、插入意向锁的加锁机制与死锁形成原理,手把手教你怎么用show engine innodb status和information_schema定位死锁,并给出加锁顺序设计等避坑清单。
|
1月前
|
SQL 运维 算法
订单表上亿行,我按用户ID拆成128片之后怎么样了
从单表几千万行慢查询的痛点出发,讲清垂直拆分与水平拆分的区别、分片键怎么选、分片算法(hash取模/range/一致性hash)怎么权衡,以及分库分表带来的分布式ID、跨片查询、分布式事务等问题,给出避免过度拆分的避坑清单。
|
1月前
|
存储 关系型数据库 MySQL
面试总问的B+树,我把磁盘IO到底怎么算的讲清楚了
从磁盘IO的底层约束讲起,逐层对比哈希、二叉、红黑树、B树与B+树,讲清MySQL为什么选B+树(矮胖树、顺序IO、范围查询、查询稳定),并用这套底层理解反过来指导覆盖索引、前缀索引、最左前缀等日常索引设计。