低选择性字段单独建索引通常无效,但联合索引中合理前置、覆盖查询或数据极度倾斜时仍有用;判断索引是否生效需看key_len和rows而非仅type=ALL;冗余索引应通过工具或SQL识别后谨慎删除。
低选择性字段加索引到底有没有用
绝大多数情况下,
条件里对低选择性字段(比如
只有 0/1,或
只有 'M'/'F')单独建索引,基本没用——MySQL 优化器大概率会直接走全表扫描,因为回表成本可能比扫一遍还高。
但“没用”不是绝对的,关键看场景:
该字段常和其他高选择性字段组合查询,且索引顺序合理(如
,
是高选择性时间字段)
查询只查索引覆盖列(
),能走
表极大(亿级),且该字段值分布极不均匀(比如 99.9% 是 0,只有 0.1% 是 1),而你总查那 0.1%,这时
可能触发索引使用
EXPLAIN 显示 type=ALL 却建了索引?先看 key_len 和 rows
别光看
是
就断定索引失效。重点看
是否非 0、
是否显著小于表总行数:
说明索引被部分使用(比如联合索引前缀匹配)
如果是几千而不是几百万,说明 MySQL 认为这个索引筛选效率尚可,只是没选它做驱动表
真正无效的典型表现是:
且
示例:
若
上有独立索引,而
是联合索引,MySQL 很可能用后者,
显示用了 2 个字段长度,哪怕
本身低选择性。
联合索引中低选择性字段放前面还是后面
放前面 ≠ 浪费,但放错位置会导致高选择性字段无法生效。核心原则:最常用于等值查询(
或
)的字段靠左,范围查询(
,
,
)字段靠右,且只能有一个范围字段在最后。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
✅ 推荐:
→ 支持
,也支持
❌ 避免:
→
单独查时完全用不上;
虽能用,但
在第二位,对排序/分页帮助有限
⚠️ 注意:
在联合索引中算等值条件,不是范围,所以仍可继续用后续字段
用 pt-duplicate-key-checker 或 INFORMATION_SCHEMA 查冗余索引
低选择性索引常和高选择性索引重叠,形成冗余。比如已有
,再单独建
索引就是浪费。
查冗余推荐两法:
用
(Percona Toolkit),它会明确标出哪些索引被另一个“包含”
手动查:
再人工比对前缀是否一致
删之前务必确认慢查日志里真没语句依赖那个单列索引——有些 ORM 自动生成的
查询,看着简单,删了就变全表扫。
WHEREstatusgender(status, create_time)create_timeSELECT status FROM t WHERE status = 1index-only scanstatus = 1typeALLkey_lenrowskey_len > 0rowskey_len = 0key = NULLEXPLAIN SELECT * FROM order WHERE status = 1 AND user_id = 12345;user_id(status, user_id)key_lenstatus=IN>BETWEENLIKE 'abc%'(status, user_id, create_time)WHERE status = 1 AND user_id = ?WHERE status = 1 AND user_id = ? AND create_time > ?(user_id, status, create_time)WHERE status = 1WHERE user_id = ? AND status = 1statusstatus IN (0,1)(user_id, status)statuspt-duplicate-key-checker --host=localhostSELECT s1.TABLE_NAME, s1.INDEX_NAME AS redundant_index,
s2.INDEX_NAME AS dominant_index
FROM INFORMATION_SCHEMA.STATISTICS s1
JOIN INFORMATION_SCHEMA.STATISTICS s2
ON s1.TABLE_SCHEMA = s2.TABLE_SCHEMA
AND s1.TABLE_NAME = s2.TABLE_NAME
AND s1.SEQ_IN_INDEX < s2.SEQ_IN_INDEX
AND s1.COLUMN_NAME = s2.COLUMN_NAME
WHERE s1.TABLE_SCHEMA = 'your_db';WHERE status = ?