# MySQL 中的索引数量是否越多越好?为什么?
8 min read
Table of Contents
MySQL 中的索引数量是否越多越好?为什么?
MySQL 索引数量是否越多越好?为什么?
【核心定义】
不是,索引数量并非越多越好,因为索引在提升查询性能的同时,也会带来额外的存储开销、写操作性能损耗和维护成本,需要在查询收益和写入代价之间取得平衡。
【关键要点】
- 查询性能提升有限:索引通过 B+Tree 结构加速数据检索,但超过查询优化器可有效利用的数量后,新增索引的边际收益递减。
- 写操作性能下降:每次 INSERT、UPDATE、DELETE 都需要更新所有相关索引,索引越多,写操作的 I/O 和 CPU 开销越大。
- 存储成本增加:每个索引都需独立存储数据(InnoDB 中为 B+Tree 副本),占用磁盘空间,影响内存中 Buffer Pool 的有效数据缓存率。
- 优化器选择负担加重:过多索引可能导致优化器评估执行计划时间变长,甚至因统计信息不准确而选择非最优索引。
【深度推导/细节】
索引的“收益-成本”平衡模型
- 收益函数:索引对查询的加速效果遵循“二八定律”,约 20% 的索引覆盖 80% 的高频查询,剩余索引收益很低。
- 成本函数:
- 写入放大效应:单行更新若涉及 N 个索引,实际需执行 N+1 次写入(数据页 + N 个索引页)。
- 页分裂频率提升:索引过多时,随机插入易导致不同索引树的页分裂,增加碎片化和维护开销。
- 临界点判断:当新增索引对查询的加速收益低于其带来的写入延迟和存储成本时,即达到“索引收益拐点”。
索引维护的隐藏成本
- Change Buffer 优化失效:对于非唯一二级索引,InnoDB 可使用 Change Buffer 延迟写入以减少随机 I/O,但索引过多会稀释该优化效果。
- 统计信息更新压力:ANALYZE TABLE 需扫描所有索引树来更新 cardinality,索引数量直接影响该操作的资源消耗和时间。
- 锁竞争加剧:索引拆分可能增加行锁升级为表锁的概率(尤其在低隔离级别下)。
【关联/对比】
与“索引覆盖”策略的关联
- 理想情况:通过精心设计的复合索引实现“覆盖索引”(Using index),可避免回表查询,此时索引虽多但价值高。
- 反面案例:大量单列索引或重复索引(如 (a,b) 和 (a) 同时存在)属于资源浪费,优化器通常无法合并使用。
与 NoSQL 系统的对比
- MySQL:索引为“可选优化路径”,需手动权衡设计。
- 某些 NoSQL(如 MongoDB):通过倒排索引、全文索引等机制,允许更多索引但采用异步更新策略,写入代价模型不同。
『面试官追问』
-
“那到底建多少个索引算合适?有具体数字吗?”
- 无绝对数字,但可参考原则:OLTP 系统通常建议单表不超过 5-7 个索引;宽表(列多)可略多,但需警惕索引列总数过多。
- 量化标准:监控
Handler_write状态变量,若索引更新占写入操作比例超过 30%,可能索引过多。
-
“复合索引和多个单列索引怎么选?”
- 复合索引优势:遵循最左前缀原则,一索引多用;可覆盖查询,避免回表;存储效率更高(前缀压缩)。
- 单列索引适用场景:列独立作为查询条件频率高;列更新频繁,复合索引维护成本过高;需支持多列任意组合查询(但需评估是否值得)。
-
“如何发现并删除无用索引?”
- 使用
sys.schema_unused_indexes视图(MySQL 5.7+)直接查看从未被使用的索引。 - 开启
performance_schema,通过events_statements_summary_by_digest分析查询模式,识别未命中的索引。 - 工具支持:
pt-duplicate-key-checker检测重复/冗余索引;pt-index-usage分析慢日志中的索引使用情况。
- 使用
-
“索引对 JOIN 查询的影响是什么?”
- 驱动表索引:用于加速 WHERE 条件过滤,减少嵌套循环次数。
- 被驱动表索引:用于 JOIN 条件匹配(如外键列),避免全表扫描。
- 风险点:多表 JOIN 时,优化器可能因索引统计信息偏差选择错误的驱动表顺序,反而降低性能。
【版本差异】
- MySQL 5.6 之前:索引条件下推(ICP)未优化,过多索引可能导致大量无效回表。
- MySQL 5.7+:支持生成列索引,可通过虚拟列对表达式建立索引,但需评估计算开销。
- MySQL 8.0+:
- 支持降序索引、函数索引,减少了为特殊排序需求创建冗余索引的必要。
- 不可见索引特性允许将索引设置为“测试状态”,便于安全评估索引效果后再决定删除或保留。
【最佳实践建议】
- 设计阶段:遵循“基于查询模式设计索引”原则,使用
EXPLAIN验证索引有效性。 - 监控阶段:定期检查索引使用率(
INFORMATION_SCHEMA.INDEX_STATISTICS)和存储占比。 - 清理策略:优先删除重复索引、前缀重叠索引、低基数(cardinality < 5%)索引。
- 特殊场景例外:数据仓库中只读表可大量使用索引;归档表可删除非核心索引以节省空间。
结论重申:索引是“以空间换时间”的典型权衡,其数量应严格受控于实际查询需求,任何冗余索引都是潜在的性能负债而非资产。