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