判断字段是否适合建索引,核心看选择性(count(distinct column)/count(*)),越接近1越高效;低于0.05~0.1时mysql常弃用索引改全表扫描;需结合查询模式、写入压力、联合索引顺序综合评估。

怎么看一个字段值的分布是否适合建索引:先算选择性
索引是否有效,核心看「选择性(Selectivity)」——也就是 count(distinct column) / count(*)。值越接近 1,说明该字段取值越分散,用索引过滤越高效;低于 0.05~0.1 时,MySQL 往往会直接放弃走索引,改用全表扫描。
实操建议:
- 用
SELECT COUNT(DISTINCT status) / COUNT(*) AS selectivity FROM orders;快速估算,注意避免在大表上直接跑(可加LIMIT抽样或查统计信息) - 字符串字段要小心前缀重复,比如
email字段整体选择性高,但只取前 5 位(LEFT(email,5))可能全是user@,选择性暴跌 - 时间字段如
created_at在「最近7天数据占90%」的场景下,单独建索引效果差,更适合和业务状态组合(如(status, created_at))
为什么 SHOW INDEX 里的 Cardinality 常不准
Cardinality 是 MySQL 对索引基数的估算值,不是实时精确统计。InnoDB 默认每 8 次修改就采样一次 B+ 树页,采样页数固定(默认 20 页),所以小表还凑合,大表偏差常达 ±30% 甚至更多。
实操建议:
- 不要依赖
SHOW INDEX FROM t的Cardinality判断是否建索引,它只是辅助参考 - 想看真实去重数,用
SELECT COUNT(DISTINCT column) FROM t(加WHERE条件匹配你的查询场景) - 更新统计信息可用
ANALYZE TABLE t;,但别频繁执行——它会锁表(8.0+ 可配innodb_stats_auto_recalc=OFF手动控制)
哪些字段即使选择性高,也不该单独建索引
高选择性 ≠ 必须建索引。还要看查询模式、写入压力、索引维护成本。
实操建议:
- 只出现在
ORDER BY但没WHERE条件的字段(如SELECT * FROM log ORDER BY trace_id),建索引纯属浪费,排序走文件排序更轻量 - 高频更新的字段(如计数器
view_count),每次UPDATE都要同步更新索引树,写放大严重 - 已经作为联合索引最左前缀的字段,再单独建索引是冗余(例如已有
(user_id, status),就不必再建user_id单列索引) - 超长文本字段(如
TEXT)必须指定前缀长度,但前缀过短(如VARCHAR(255)只建INDEX(col(10)))会导致选择性归零
联合索引顺序怎么排:看 WHERE 条件和 ORDER BY 的实际组合
顺序不是按「哪个字段选择性更高」排,而是按「查询中出现的顺序 + 匹配方式」定。等值条件(=)优先于范围条件(>, LIKE 'abc%'),而范围之后的字段无法走索引。
示例:查询是 WHERE category = ? AND price > ? ORDER BY create_time DESC,最优索引是 (category, price, create_time) —— 不是把 create_time 放前面,因为 price > 是范围,它后面的字段无法用于排序加速。
实操建议:
- 用
EXPLAIN看key_len和Extra字段:如果key_len比预期小,说明后缀字段没用上;如果Extra出现Using filesort或Using temporary,大概率是顺序错了 - 区分
LIKE类型:LIKE 'abc%'能用索引,LIKE '%abc'完全不能,后者别指望靠调整顺序补救 - 有
IN列表时(如WHERE status IN ('paid','shipped')),它被视为等值条件,可放联合索引中间,但别放最后——后面字段依然失效
真正难的不是算基数,而是把字段分布、查询语句结构、执行计划输出这三者串起来看。很多人卡在 EXPLAIN 里 type=range 却 key_len 异常,其实只是联合索引把范围条件写太靠前了。











