mysql对count()优先走二级索引,因其叶子节点仅存索引列值和主键,比聚簇索引更轻量、b+树更矮胖、i/o更少;count()和count(1)均被优化为遍历最短二级索引,无需判null;count(字段)是否走二级索引取决于该字段是否有索引及是否需回表判null。

MySQL 为什么对 COUNT(*) 优先走二级索引?
因为二级索引更轻量:叶子节点只存「索引列值 + 主键」,不存整行数据,B+ 树更矮胖,I/O 次数更少。InnoDB 优化器在统计行数时,只要能覆盖查询需求(比如 COUNT(*) 不需要读取任何字段值),就会选最短的二级索引——哪怕只是 id 字段的单列索引,也比聚簇索引节省大量磁盘读。
COUNT(1) 和 COUNT(*) 真的会走二级索引吗?
会,而且行为完全一致。MySQL 把 COUNT(*) 当作 COUNT(0) 处理,COUNT(1) 中的常量表达式永远非 NULL,两者都不需要判断字段是否为 NULL,因此优化器只需遍历索引记录数即可。只要表上存在任意二级索引,COUNT(*) 和 COUNT(1) 就大概率跳过聚簇索引,直接扫那个最短的二级索引。
COUNT(字段) 会不会也走二级索引?
取决于字段是否有索引,以及该索引是否能避免回表:
- 如果字段是主键,且存在更短的二级索引(比如
idx_status),优化器仍可能选二级索引——但需注意:COUNT(id)要检查主键是否为 NULL(尽管主键不可能为 NULL,Server 层仍做判断),所以实际开销略高于COUNT(*) - 如果字段有二级索引(如
name上建了idx_name),COUNT(name)会走该索引,但每条记录仍要取name值判 NULL,不能省掉字段读取 - 如果字段无索引,
COUNT(字段)只能全表扫描聚簇索引,逐行读取并判断该字段是否为 NULL
怎么验证当前 COUNT 查询走了哪个索引?
用 EXPLAIN 看 key 列和 Extra 列:
-
key: PRIMARY表示走了聚簇索引 -
key: idx_name表示走了名为idx_name的二级索引 -
Extra: Using index是关键信号——说明用了覆盖索引,没回表 -
Extra: Using where; Using index表示带 WHERE 条件且仍走索引覆盖
特别注意:即使你只建了一个二级索引,EXPLAIN SELECT COUNT(*) FROM t 也可能显示 key: NULL ——这是 MySQL 8.0+ 的“索引合并优化”或“隐式索引选择”行为,不代表没走索引,需结合 Handler_read_next 等状态变量进一步确认。
真正容易被忽略的是:二级索引虽快,但它的“快”建立在「无 WHERE 条件」或「WHERE 条件能命中同一索引」的前提下;一旦加了复杂过滤条件,优化器可能放弃二级索引,转而走聚簇索引甚至临时文件排序。











