为什么低选择性索引反而拖慢查询
当
WHERE条件字段的重复值过多(比如
status只有 '0'/'1',或
gender只有 'M'/'F'),MySQL 很可能直接放弃走索引,改用全表扫描——因为走索引要回表多次,成本比扫一遍还高。
典型表现:
EXPLAIN中
type是
ALL或
index,
key为空,
rows接近表总行数。 索引选择性 =
COUNT(DISTINCT column) / COUNT(*);低于 0.05 通常视为低选择性 即使加了索引,优化器也可能忽略它——这不是 bug,是成本估算结果
FORCE INDEX强制走索引往往更慢,别试
用覆盖索引绕过回表开销
如果查询只涉及少量字段,且这些字段都在索引里,MySQL 就不用回主键查找数据行。这对低选择性字段特别有效。
例如:查询
SELECT id, status FROM orders WHERE status = 1,可建联合索引:
ALTER TABLE orders ADD INDEX idx_status_id (status, id)。 顺序很重要:低选择性字段放前面(
status),高选择性字段放后面(
id) 避免
SELECT *,只查真正需要的列 覆盖索引对
ORDER BY和
LIMIT也友好,能避免 filesort
用分区或物化视图替代单字段索引
对固定取值、高频过滤但低选择性的字段(如
tenant_id、
region),单列索引意义不大,可考虑按该字段做范围/列表分区。
MySQL 8.0+ 支持不可见索引和降序索引,但解决不了根本问题;真正有效的思路是把“筛选逻辑”前置。
按时间 + 状态组合分区:PARTITION BY LIST COLUMNS(status) ...,让引擎快速跳过无关分区 用汇总表替代实时查:
CREATE TABLE orders_summary AS SELECT status, COUNT(*) cnt FROM orders GROUP BY status应用层缓存状态统计类结果,比反复查低选择性字段更可靠
什么时候该删掉这个索引
低选择性索引不仅没用,还会拖慢写入(每次 INSERT/UPDATE 都要维护索引树)、增大内存占用、干扰优化器判断。
运行
SELECT * FROM sys.schema_unused_indexes(需启用 performance_schema),或查
information_schema.STATISTICS配合慢日志分析实际命中率。 连续一周没有被
WHERE、
JOIN、
ORDER BY使用过的索引,大概率可以删 唯一约束或外键依赖的索引不能删,哪怕选择性低 删除前先用
DROP INDEX idx_name ON tbl_name;测试,观察慢查询是否回升
低选择性本身不是病,硬给它配索引才是。关键是看查询模式是否真需要靠它驱动,还是只是惯性加索引。
