跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

为什么SQL连接查询在某些字段上速度极快_分析索引选择度Cardinality

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
—— 其他所有优化都该建立在这个事实之上。

相关文章