MySQL优化器会主动跳过低区分度索引,因全表扫描成本更低;type=ALL且key=NULL表明索引被放弃;区分度需用COUNT(DISTINCT)/COUNT计算,低于0.01则不建议单列建索引。

MySQL优化器会主动跳过这类索引,不是它“不支持”,而是算完账发现走索引比全表扫描还慢。
EXPLAIN看到type=ALL、key=NULL就说明索引被忽略了
执行EXPLAIN SELECT * FROM user WHERE gender = 'male';
- 如果
type是ALL、key是NULL,代表优化器彻底放弃该索引 - 这不是bug,是基于
Handler_read_key(读索引页)和Handler_read_rnd_next(回表取行)双重随机I/O成本的理性判断 - 比如
is_deleted = 0匹配99.5%的行,优化器宁愿顺序扫一遍表,也不愿在索引里翻一半叶子节点,再随机跳着读99.5%的主键页
区分度必须自己算,别信SHOW INDEX里的Cardinality
SHOW INDEX显示的Cardinality是采样估算值(默认只看8个页),误差大、更新滞后
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 真正该跑的是:
SELECT COUNT(DISTINCT gender) / COUNT(*) AS selectivity FROM user; - 结果低于
0.01(即1%)基本可判为低区分度;高于0.9才算高价值索引候选 - 状态字段如
status若90%是'processing',区分度就是0.2,单列建索引意义有限
单独建索引反而拖慢写入和联合索引效果
低区分度字段一旦进了索引体系,副作用往往比查询收益更明显
- 每次
INSERT/UPDATE都要维护该索引页,而低基数列索引页分裂频繁、缓存命中率低 - 如果进了联合索引但放错位置(比如
KEY idx_status_user_id (status, user_id)),那WHERE user_id = ?就完全用不上——最左前缀失效 - 即使
WHERE status = 'done' AND create_time > '2026-01-01'这种查询,只要status在联合索引里排第一,优化器仍可能因区分度太低而弃用整个索引
真正要加速这类字段的查询,得看场景:是否能覆盖查询(只查索引字段)、是否能配合高区分度列做联合索引、是否真有极小结果集(比如is_deleted = 1只有0.1%数据)。硬建单列索引,基本等于给优化器出难题。










