索引是否有效取决于cardinality值高低:接近总行数(≥95%)说明区分度高,适合建索引;<10%则单列索引意义不大;低区分度字段应置于联合索引后缀,如(created_at, status),并用explain验证实际使用情况。

索引有没有用,先看 Cardinality 值够不够高
MySQL 的 SHOW INDEX FROM table_name 里那个 Cardinality 字段,不是“有多少行”,而是“该列值大概有多少个不同取值”。它直接影响优化器是否愿意走索引。如果 Cardinality 只有几百,而表有百万行,那这个索引大概率被忽略——因为扫描索引再回表,比直接全表扫还慢。
-
Cardinality接近表总行数(比如 95% 以上),说明这列区分度高,适合建索引 - 如果
Cardinality小于总行数的 10%,基本可以判定:加单列索引意义不大 - 注意:
Cardinality是采样估算值,执行ANALYZE TABLE table_name可刷新,但不会实时更新
区分度低的字段硬加索引,反而拖慢写入
比如 status 只有 'active'、'inactive'、'pending' 三个值,就算加上索引,查询时优化器大概率走全表扫描;更麻烦的是,每次 INSERT/UPDATE 都要维护这个索引 B+ 树,写放大明显。
- 常见陷阱:给布尔型、枚举型、状态码字段单独建索引,却不结合查询条件中的其他过滤字段
- 替代方案:把低区分度字段放在联合索引的**后缀位置**,比如
(created_at, status),靠前缀created_at拉高整体选择性 - 验证方法:用
EXPLAIN看type是否为ref或range,而不是ALL
联合索引的顺序怎么排?看 WHERE 条件里的等值匹配和范围查询
索引生效不只看有没有,更看字段在 WHERE 中的使用方式。MySQL 只能高效利用索引的最左前缀,一旦遇到范围查询(>、BETWEEN、LIKE 'abc%'),后面的字段就失效了。
- 优先把等值条件(
=、IN)字段放前面,比如WHERE user_id = ? AND status = ? AND created_at > ?→ 索引应为(user_id, status, created_at) - 如果
WHERE中只有status = ? AND created_at > ?,那(status, created_at)效果很差,因为status区分度低,且范围查询后无法利用created_at的有序性做进一步剪枝 - 用
EXPLAIN FORMAT=JSON查看used_key_parts,确认实际用了索引的哪几段
SELECT COUNT(*) 和 COUNT(非空字段) 对索引依赖完全不同
很多人以为加了索引就能加速统计,其实不然。COUNT(*) 在 InnoDB 中本质是遍历聚簇索引,跟二级索引无关;但 COUNT(某个有索引的 NOT NULL 字段) 可能走覆盖索引,快得多。
- 如果只想知道行数,且能接受近似值,
SHOW TABLE STATUS LIKE 'table_name'里的Rows字段更快(但不准) - 精确统计大表:优先考虑带
WHERE条件的COUNT(),并确保条件能命中高区分度索引前缀 - 别给
id单独建索引加速COUNT(*)—— 聚簇索引本身就是按id排序的,额外索引纯属冗余
区分度不是拍脑袋定的,得看 Cardinality 和真实查询模式;联合索引顺序也不是语法习惯问题,而是执行计划能不能用上的关键。很多慢查优化失败,不是没建索引,是建了但根本没被选中。










