索引越多查询越快?别信这个误区
索引不是越多越好,MySQL 在写入(
INSERT、
UPDATE、
DELETE)时要同步更新所有相关索引,索引数量多会显著拖慢 DML 性能。更隐蔽的问题是:过多索引会增大
buffer_pool压力,挤占真正热数据的缓存空间,反而让查询变慢。
常见错误现象包括:
单表索引数超过 10 个,且很多是单列冗余索引(比如已有(a, b),又单独建了
a)
SHOW INDEX FROM table_name显示大量
Cardinality极低(接近 0 或远小于表行数)的索引 慢查询日志里反复出现
Using index condition但实际没走索引,或
Using filesort/
Using temporary频发
如何识别并删除无效/重复索引
MySQL 8.0+ 可直接用
sys.schema_unused_indexes视图查长期未被使用的索引;5.7 及以下需结合
performance_schema手动分析。但更实用的是先做静态扫描: 用
pt-duplicate-key-checker(Percona Toolkit)一键检测重复和冗余索引,例如:
(user_id)和
(user_id, status)共存时,前者通常可删 检查
WHERE条件中是否总以某列为前导列,若从不单独查
b,只查
a = ? AND b = ?或
a = ?,那
(a, b)足够,不必另建
(b)唯一性要求高的字段(如
UNIQUE约束,它自带索引且语义清晰,避免为“去重”再加普通索引
复合索引顺序怎么排才不踩坑
最左前缀原则不是死规则,而是执行器匹配索引项的路径限制。关键看查询模式中列的过滤强度和排序需求:
高选择性列(如user_id,区分度 > 90%)放最左,能快速缩小扫描范围 等值查询列(
=)优先于范围查询列(
>、
BETWEEN),因为范围之后的列无法用于索引查找,只能用于过滤。例如查询
WHERE a = 1 AND b > 10 AND c = 5,索引应为
(a, b, c),不是
(a, c, b)如果含
ORDER BY,且无
filesort,需确保排序字段在索引中连续且方向一致(如
ORDER BY a ASC, b DESC要求索引为
(a, b)且 MySQL 8.0+ 支持降序索引)
什么时候该用覆盖索引而不是加字段
当查询只涉及少数几个字段,且它们能被一个索引全部包含时,用覆盖索引比回表更高效。但要注意代价:
覆盖索引本质是把数据“复制”进索引页,VARCHAR(500)字段加进索引会让 B+ 树节点急剧膨胀,可能引发更多磁盘 I/O 优先覆盖高频、轻量字段(如
id,
status,
created_at),避免覆盖大文本或 JSON 字段 用
EXPLAIN看
Extra列是否含
Using index—— 这才是真覆盖;若出现
Using index condition,说明用了 ICP(索引条件下推),但仍有回表
真正难的不是建索引,而是持续观察:上线后盯紧
Handler_read_next/
Handler_read_rnd_next的增长比例,定期跑
SELECT * FROM sys.statement_analysis看哪些索引被高频使用、哪些常年沉默。索引不是设好就完事的配置项,它是随业务查询模式演化的活体结构。
