为什么MySQL InnoDB中带LIKE '%%'的COUNT查询比无过滤COUNT更快?
InnoDB 下两类 COUNT 查询性能差异的核心原因
首先明确基础前提:InnoDB 不像 MyISAM 内置了全局表行数缓存,所有 COUNT 统计都需要实际扫描存储结构计算,不存在直接读预存行数的捷径。
第一组无过滤查询慢的原因
你跑的这两条无任何 WHERE 条件的语句:
SELECT COUNT(*) FROM tb_user SELECT COUNT(idx_user_name) FROM tb_user
对应执行计划走的是全表扫描(type=ALL):优化器做成本估算时,没有选择idx_user_name二级索引,而是直接扫描聚簇索引(主键索引)统计行数。
- 聚簇索引的叶子节点存的是整行所有字段的完整数据,100万行的聚簇索引占用的磁盘空间大,扫描过程需要加载大量数据页到内存,IO成本很高,所以耗时达到2.5秒。
- 因为两个语句都走了同一条全表扫描的执行路径,所以耗时几乎完全一致。
第二组带LIKE '%%'查询快的原因
加了WHERE idx_user_name LIKE '%%'的两条语句,看起来条件完全没过滤效果(前后通配符会匹配所有非NULL的字段值,等于不筛掉任何行),但实际给优化器传递了明确的信号:本次查询只需要访问idx_user_name这个索引包含的字段就能完成计算。
- 这时候优化器会选择覆盖索引扫描,直接遍历
idx_user_name这个唯一二级索引的叶子节点统计行数。 - 二级索引的叶子节点只存索引列值 + 对应的主键ID,不存其他无关的行字段,单个数据页能存的索引条目数是聚簇索引的好几倍,100万行的二级索引整体体积小很多,扫描需要加载的磁盘页极少,IO成本骤降,所以耗时只有0.3秒。
几个补充说明:
- 无过滤条件时优化器不主动选二级索引,是MySQL 5.7及更早版本的常见成本计算偏差:优化器错误给二级索引路径加上了回表的成本预估,但COUNT统计根本不需要回表取整行数据,才会选错执行路径;部分8.0版本已经修正了这个逻辑,但统计信息不准时还是可能出现同类问题。
- 想要无过滤COUNT也达到0.3秒的性能,可以手动强制走索引:
SELECT COUNT(*) FROM tb_user FORCE INDEX(idx_user_name),执行效率和加LIKE '%%'完全一样。- 这里
COUNT(*)和COUNT(idx_user_name)性能没有差异,是因为InnoDB对COUNT(*)做了专门优化,不需要判断字段是否为空,碰到行就直接计数;走二级索引时COUNT(索引列)只需要判断索引列非空就计数,两者计算成本几乎没有差别。
内容的提问来源于stack exchange,提问作者zby001
相关产品推荐
相关产品推荐

