跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

mysql低选择性索引是否有用_mysql索引优化方案

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

相关文章