线上发现索引数量不断增加但查询性能没有改善,写入和磁盘开销反而上升,你会如何识别冗余索引、低效索引和真正需要保留的索引?
免费版我不会单纯按索引数量判断,而是结合真实 SQL 负载、索引定义、执行计划和写入成本来清理。
1. 先盘点索引和来源
通过 SHOW CREATE TABLE、information schema.statistics,或者对应数据库的系统视图,记录每个索引的列顺序、是否唯一、索引大小、基数、创建来源以及所属表。重点检查:
完全重复的索引,例如两个索引列顺序和属性完全相同; 左前缀重复,例如已有 (a,b,c),又存在普通 (a),如果没有独立的排序、覆盖或唯一约束需求,后者可能是冗余的; 组合索引列顺序不合理,导致常用条件无法使用左前缀; 只为了覆盖少量查询而创建的大索引,实际收益很低; 由 ORM、迁移脚本或重复发布自动创建的索引。
但不能看到左前缀就直接删除,还要确认唯一性、排序方向、覆盖列、分区以及是否承担外键或约束支持。主键、唯一索引、外键依赖的索引一般要单独评估。
2. 判断索引是否真的被使用
结合慢查询日志、审计日志、performance schema、数据库的索引统计视图,以及业务高峰期的 SQL 采样,统计索引访问次数、关联 SQL、扫描行数和延迟。不同数据库可以使用 sys.schema unused indexes、pg stat user indexes 或对应的 DMV。
这里不能只看索引命中次数为零就删除,因为统计信息可能在重启后被清空,低频的月末结算、报表和故障处理 SQL 也可能依赖它。至少要覆盖完整业务周期,并确认统计采集时间足够长。
3. 用执行计划判断索引是否高效
对代表性 SQL 使用 EXPLAIN 或 EXPLAIN ANALYZE,重点看是否真正走了该索引、实际扫描行数、回表数量、过滤比例、排序和临时表情况。如果索引选择性很差,例如状态字段只有几个值,优化器可能认为全表扫描更便宜;如果存在函数计算、隐式类型转换、前缀通配符,索引也可能无法有效使用。
还要检查统计信息是否过期、数据分布是否倾斜,以及索引列顺序是否符合最常见的过滤、连接和排序条件。一个被使用但只能过滤掉很少数据的索引,也可能属于低效索引。
4. 同时评估写入和资源成本
索引不是免费的。插入、删除以及更新索引列时都要维护索引,索引过多会增加 CPU、磁盘空间、Buffer Pool 或缓存压力,还可能放大锁竞争和写入延迟。因此我会把索引大小、更新频率、写入 QPS、磁盘 IO 和 P95/P99 写延迟一起看。
通常可以这样分类:
完全重复、没有约束意义的索引:优先删除; 使用次数很少、选择性差、体积大且写入成本高的索引:列为候选删除对象; 高频查询中能显著减少扫描量、支持高价值接口或核心报表的索引:保留并优化列顺序; 主键、唯一约束、外键依赖或少数关键低频任务需要的索引:保留,但要确认其必要性。
5. 分阶段验证,不能一次性批量删除
