navicat中rows值不可信,因其是mysql优化器基于默认采样20页的粗略估算,遇数据倾斜、函数条件、隐式转换或非最左前缀查询时严重失真,误差常超±40%;应重点观察type、key和extra字段,并通过explain format=json查rows_examined或开启慢日志获取真实扫描行数。
navicat 里看到的 rows 值基本不可信,它只是 mysql 优化器基于采样统计的粗略预估,尤其对 innodb 表,误差常超 ±40%,不能用来判断性能瓶颈或索引是否生效。
为什么 rows 在 Navicat 的 EXPLAIN 里严重失真
MySQL 的 EXPLAIN 不执行真实扫描,而是靠 information_schema.STATISTICS 和内存中缓存的采样页估算。InnoDB 默认每张表只采样 8 个索引页(innodb_stats_persistent_sample_pages = 20 是 5.6+ 默认值),数据分布不均时,估算直接崩坏。比如一个时间字段上存在大量空值或倾斜值,rows 可能报 100,实际扫 50 万行。
- MyISAM 的
TABLE_ROWS是精确缓存值,但 Navicat 的 EXPLAIN 仍走优化器估算路径,不复用该值 - 跨库查询时,
EXPLAIN完全看不到其他库的统计信息,对应表的rows直接标为NULL或 1 - 如果表刚被大批量 INSERT/DELETE,而没运行
ANALYZE TABLE,采样数据就更过时
rows 失真时,真正该盯住的字段
别盯着 rows 看数字大小,重点看三处是否匹配业务直觉:
-
type:是不是ALL或index?如果是,说明没走有效索引,哪怕rows=1也是假象 -
key:是否为NULL?为空代表优化器放弃所有可用索引,比rows失真更危险 -
Extra:含Using filesort或Using temporary就意味着排序/分组没走索引,和rows多少无关
怎么拿到接近真实的扫描行数
必须绕过 EXPLAIN 的估算,触发一次真实执行并捕获服务端指标:
- MySQL 5.7+:执行
EXPLAIN FORMAT=JSON SELECT ...,看输出里的execution_time和rows_examined字段(注意不是rows) - 开启慢日志:
SET long_query_time = 0,再运行原语句,去 Navicat「查询分析器」查rows_examined和query_time - PostgreSQL:必须写
EXPLAIN (ANALYZE, BUFFERS) SELECT ...,Buffers:行和Execution Time:才是实测数据
容易被忽略的底层干扰点
即使你拿到了真实 rows_examined,它也可能被隐藏因素扭曲:
- InnoDB 的 MVCC 一致性读会回滚段找旧版本,
rows_examined统计的是“访问的行数”,不是“最终返回的行数” - 如果 WHERE 条件中有函数(如
DATE(create_time))或隐式转换(user_id = '123'但字段是INT),优化器可能放弃索引下推,导致rows_examined暴增 - Navicat 默认开启
autocommit=1,每条语句都是独立事务,高并发下锁等待、间隙锁冲突也会让实际耗时远超rows_examined所暗示的水平











