mysql优化器会主动跳过低区分度字段的单列索引,因随机i/o开销大于全表扫描;区分度低于0.01时索引几乎无效,应优先考虑覆盖查询或合理设计联合索引。

MySQL 优化器会直接跳过这个索引,走全表扫描反而更快——这不是配置问题,是它算过账后主动放弃。
EXPLAIN 显示 type=ALL 就说明索引被无视了
执行 EXPLAIN SELECT * FROM user WHERE gender = 'male'; 后,如果 type 是 ALL、key 是 NULL,就代表优化器压根没考虑用这个索引。这不是 bug,也不是 MySQL “不支持”,而是它估算出:走索引要先读索引页(Handler_read_key),再根据主键随机跳着取行(Handler_read_rnd_next),而性别字段匹配约 50% 行时,这种随机 I/O 总开销远超顺序扫一遍表。
区分度低于 0.01 的字段基本不该单建索引
别信 SHOW INDEX 里的 Cardinality,那是采样估算,误差大、更新滞后。真正该跑的是:
SELECT COUNT(DISTINCT gender) / COUNT(*) AS selectivity FROM user;
结果解释:
- 低于
0.01(即 1%)→ 单列索引几乎无效,空间和写入成本白耗 - 在
0.2左右 → 效果有限,单独建意义不大 - 高于
0.9→ 才算高价值候选,比如邮箱、手机号
status 或 is_deleted 这类字段建了索引反而拖慢写入
低区分度字段一旦建了单列索引,会带来三重隐性代价:
- 每次
INSERT或UPDATE都要维护该索引页,而低基数列索引页分裂频繁、缓存命中率低 - 若它进了联合索引但放在最前面(如
KEY idx_status_user_id (status, user_id)),那WHERE user_id = ?就完全失效——最左前缀断了 - 即使查询带
status和时间范围,优化器仍可能因整体 selectivity 太低而弃用整个索引
真要加速这类查询,优先试试覆盖或组合方案
硬建单列索引是下策。更务实的路径取决于实际 SQL 模式:
- 只查索引字段本身(如
SELECT is_deleted FROM orders WHERE is_deleted = 1),可避免回表,type: index也能接受 - 把它当联合索引的后缀列,且前缀列区分度足够高,例如
KEY idx_user_id_is_deleted (user_id, is_deleted) - 匹配数据极少时(如
is_deleted = 1仅占 0.1%),索引才有明显收益,但必须实测rows和Handler_read_rnd_next变化
最容易被忽略的是:线上加索引容易锁表,而联合索引里字段顺序错了,整个索引就等于废掉——顺序、场景、验证,一个都不能跳。











