mysql不推荐在低区分度列加索引,因其导致b+树退化为宽浅结构,使查找退化为顺序扫描,回表开销大且优化器常弃用;cardinality为采样估算值,易失真,真实区分度低于5%时索引基本无效。

索引不是加了就快,关键看字段的 Cardinality —— 它直接决定 MySQL 会不会用这个索引、用了能过滤掉多少行。低 Cardinality 的字段(比如 del_flag、gender)建索引,基本等于白搭,甚至拖慢写入。
怎么看字段的 Cardinality 值?
别猜,直接查 SHOW INDEX FROM your_table,重点关注 Cardinality 列。这个值是 MySQL 对“该索引列中不同值数量”的估算,不是精确值,但足够做判断。
-
Cardinality接近表总行数(比如 100 万行表,Cardinality是 99 万),说明这列几乎唯一,适合做主键或单列索引 -
Cardinality只有几十或几百,而表有上百万行,说明这列重复极高(如状态码、开关位),索引大概率被优化器忽略 - 执行
ANALYZE TABLE your_table可刷新统计信息,让Cardinality更准——尤其在大批量导入/删除后必须做
为什么 JOIN 条件字段 Cardinality 低就慢?
JOIN 本质是匹配:MySQL 要从驱动表每行出发,在被驱动表里找匹配项。如果被驱动表的 JOIN 字段 Cardinality 极低(比如 status 只有 3 个值),即使建了索引,MySQL 也预估要回表读取大量重复值,最终放弃索引走全表扫描。
- 典型表现:
EXPLAIN中type是ALL或index,key为NULL - 更隐蔽的情况:复合索引里把低
Cardinality字段放最左,比如(status, user_id),由于最左前缀失效,整个索引对user_id单独查询无效 - 解决思路不是“给 status 加索引”,而是换驱动表、或先用高
Cardinality字段过滤再 JOIN
Cardinality 和选择性 Selectivity 的关系
Selectivity = Cardinality / Total_Rows,这是量化索引效率的核心公式。它告诉你这个索引平均能排除多少数据。
- 选择性 > 0.7(比如
phone字段),索引效果极佳;0.2~0.7(如unit_id)可接受;del_flag)基本不值得单独建索引 - 计算示例:
SELECT COUNT(DISTINCT product_id)/COUNT(*) AS selectivity FROM order_details;—— 如果结果是0.85,那product_id就是优质索引候选 - 注意:MySQL 在预估返回行数超总行数 30% 时,会主动放弃索引走全表——这就是为什么查 “最近 30 天” 可能走索引,查 “最近 90 天” 就变全表
真正卡住性能的,往往不是没建索引,而是建在了 Cardinality 极低的字段上,还误以为“有索引=快”。验证它,只需一条 SHOW INDEX 和一次 ANALYZE TABLE —— 其他所有优化都该建立在这个事实之上。










