因为辅助索引B+树叶节点仅存索引列值和主键ID,体积更小、更紧凑,I/O更少;优化器在无WHERE/GROUP BY时自动选择最窄且非空的二级索引进行全索引扫描,避免读取整行数据。

为什么COUNT(*)选辅助索引比主键索引快?
因为辅助索引(二级索引)的B+树叶子节点只存索引列值 + 主键ID,而主键索引(聚簇索引)叶子节点存的是整行数据。COUNT(*)只需统计行数,不读字段内容,优化器自然倾向扫描更小、更密集的索引树——减少I/O和内存拷贝,速度就上去了。
哪些辅助索引会被优化器选中?
优化器会评估所有可用索引的成本,优先选择“最窄”的那个。但不是所有辅助索引都靠谱:
-
INDEX idx_status (status)这类单列短类型索引通常最优,体积小、覆盖全表 -
INDEX idx_ab (a, b)如果a是VARCHAR(200),索引体积可能超过主键,反而更慢 -
INDEX idx_a (a(10))前缀索引在部分MySQL版本中不被识别为有效计数索引,EXPLAIN显示Using index也不代表真走索引扫描 - 没有辅助索引时,优化器只能退回到主键索引,哪怕表很宽也得硬扫
COUNT(*)和COUNT(1)、COUNT(id)的执行路径差异
三者语义不同,但InnoDB下优化行为高度趋同:
-
COUNT(*)和COUNT(1):优化器几乎总能自动选最小辅助索引,且不取字段值,纯计数 -
COUNT(id):虽然id是主键且NOT NULL,但优化器仍可能把它当作普通列处理;若强制FORCE INDEX (PRIMARY),就会读整行数据,变慢 - 真正拖慢的是
COUNT(name)这类允许NULL的字段——即使有索引,也要逐项判断是否为NULL,多一次检查开销
容易被忽略的现实约束
别只盯着索引存在与否。以下情况会让“选辅助索引”失效或反效果:
- 辅助索引列区分度极低(比如
status95%都是1),优化器可能判定走索引成本高于全表扫描,直接放弃 - 表刚经历大批量删除,索引统计信息过期,
ANALYZE TABLE没跑过,优化器误判 - 查询带
WHERE但条件列无索引,整个COUNT逻辑退化为带过滤的全表扫描,辅助索引优势归零 - InnoDB的MVCC机制决定了:每次COUNT都必须按当前事务可见性实时计算,不存在缓存行数这种捷径


















