mysql如何避免索引过多导致性能下降_mysql合理索引设计

来源:这里教程网 时间:2026-02-28 20:47:39 作者:

索引越多查询越快?别信这个误区

索引不是越多越好,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)
唯一性要求高的字段(如
email
)优先用
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
看哪些索引被高频使用、哪些常年沉默。索引不是设好就完事的配置项,它是随业务查询模式演化的活体结构。

相关推荐