mysql索引选择性如何理解_mysql优化设计分析

来源:这里教程网 时间:2026-02-28 20:47:18 作者:

索引选择性到底是什么,为什么它比“有没有索引”更重要

索引选择性不是“这个字段能不能建索引”,而是“建了之后,MySQL 能不能靠它快速缩小查找范围”。它的本质是:不重复的索引值数量 ÷ 表总行数。比如

email
字段有 100 万行、99.9 万不同值,选择性 ≈ 0.999;而
status
只有
'active'
'inactive'
两个值,选择性只有 0.000002 —— 后者即使建了索引,优化器也大概率弃用。

选择性 > 0.1 通常值得建索引; 唯一索引(如主键、
UNIQUE
)选择性恒为 1,效果最稳
SHOW INDEX FROM table_name
中的
Cardinality
是采样估算值,不准,不能直接当选择性用 —— 要自己算:
SELECT COUNT(DISTINCT col) / COUNT(*) FROM table_name

复合索引里字段顺序怎么排?别再凭感觉写了

字段顺序决定索引能覆盖哪些查询,核心原则就一条:把选择性高的列放前面。因为 MySQL 的 B+ 树索引是先按第一列排序,再在相同值内按第二列排序,依此类推。如果高选择性列在后,前导列区分度低,树的分支就“胖”而不“深”,扫描行数反而更多。

错误示范:
INDEX idx_status_created_at (status, created_at)
——
status
可能只有 3–5 个值,开头就卡死在大块数据里
正确做法:
INDEX idx_created_at_status (created_at, status)
—— 时间戳天然高选择性,能快速定位到某天/某小时的数据块,再筛状态
注意:如果查询中含范围条件(如
created_at > '2025-01-01'
),它后面的字段就失效了 —— 所以范围列尽量放最后

为什么 EXPLAIN 显示走了索引,但查询还是慢?可能是“假走索引”

优化器说“我用了索引”,不代表真高效。常见假象是:索引被选中,但

rows
值巨大,或者
Extra
出现
Using where; Using index
却仍回表多次。根本原因往往是索引没覆盖查询所需字段,或选择性太差导致实际扫描行数远超预期。

SELECT name, email FROM users WHERE status = 'active'
,只给
status
建索引 → 必须回表取
name
email
→ 慢
改成覆盖索引:
INDEX idx_status_name_email (status, name, email)
→ 全部字段都在索引里,无需回表
EXPLAIN FORMAT=JSON
used_columns
key_parts
,确认是否真正用到了全部索引字段

基数不准怎么办?别让过期统计拖垮执行计划

MySQL 的索引统计(

Cardinality
)默认靠采样估算,且只在数据变更超 1/M(默认 M=20,即 5%)时才自动更新。这意味着:大批量删旧数据后,索引统计还显示“1000 万行”,但实际只剩 10 万,优化器仍按老数据做成本判断,可能错选全表扫描。

手动更新统计:
ANALYZE TABLE table_name
(InnoDB 下轻量,不锁表)
长期高频写入场景,建议开启持久化统计:
SET GLOBAL innodb_stats_persistent = ON
,避免重启后丢失
极端情况可临时强制索引:
SELECT * FROM t USE INDEX (idx_col)
,但这是兜底手段,不能替代统计修复

真正容易被忽略的点是:索引选择性不是静态指标,它随数据分布实时变化。今天高选择性的字段,明天批量导入相同值后可能就废了——所以定期用

SELECT COUNT(DISTINCT...)
抽样验证,比盲目加索引更有效。

相关推荐