低选择性索引被误用会导致大量回表,识别需查CARDINALITY/总行数<1%、过滤比>15%、Handler_read_rnd_next暴涨;优化需刷新统计、避免索引列函数、调整复合索引顺序;极端倾斜键可拆逻辑处理。

低选择性索引本身不是问题,问题在于它被误用或强用——MySQL 优化器看到索引就走,结果回表扫了几十万行。
怎么快速识别索引选择性差
别光看 EXPLAIN 显示 type=ref 就以为没问题。重点查三件事:
- 用
SHOW INDEX FROM table_name看CARDINALITY值,再除以总行数;低于 1%(比如千万级表CARDINALITY < 100000)就是高风险 - 执行
SELECT COUNT(*) FROM t WHERE low_col = 'x'和COUNT(*)对比,过滤比 >15% 基本不值得走索引 - 监控状态变量:
Handler_read_rnd_next暴涨 +Handler_read_next平稳,说明大量随机回表,正是低选择性索引拖累的典型信号
为什么 status = 1 的索引还被选中
不是优化器傻,是它被“骗”了:统计信息过期、WHERE 条件混用函数、或复合索引里把倾斜字段放最左,都会让它误判成本。
- 执行
ANALYZE TABLE table_name强制刷新统计,别依赖自动采样(尤其数据批量导入后) - 避免在索引列上用函数:
WHERE UPPER(status) = 'ACTIVE'会让索引失效;改成WHERE status = 'active'并配合适当 collation - 如果
status95% 是1,就别把它放复合索引最左;换成(user_id, created_at, status),让高区分度字段先筛
倾斜字段到底该不该删索引
不能一刀切。有些场景下,哪怕只有两个值,它也能救命。
- 查
information_schema.STATISTICS看SEQ_IN_INDEX,确认该字段是不是复合索引最左前缀;如果不是,删掉影响小 - 如果查询带
ORDER BY created_at LIMIT 10且常查status = 'pending',那这个低选择性字段配合排序能跳过大量数据,保留反而更快 - 对极端倾斜键(比如某
user_id占全表 40% 行),考虑拆逻辑:单独写一条 SQL 处理它,其余走通用索引路径
真正难的不是判断“要不要索引”,而是理解「这一条查询实际要读多少行」——rows 字段只是估算,Handler_read_rnd_next 才是真实 I/O 开销。很多慢查表面走了索引,其实是在用索引加速全表扫描。


















