# MySQL 中的索引数量是否越多越好?为什么?

8 min read
Table of Contents

MySQL 中的索引数量是否越多越好?为什么?

MySQL 索引数量是否越多越好?为什么?

【核心定义】

不是,索引数量并非越多越好,因为索引在提升查询性能的同时,也会带来额外的存储开销、写操作性能损耗和维护成本,需要在查询收益和写入代价之间取得平衡。

【关键要点】

  1. 查询性能提升有限:索引通过 B+Tree 结构加速数据检索,但超过查询优化器可有效利用的数量后,新增索引的边际收益递减。
  2. 写操作性能下降:每次 INSERT、UPDATE、DELETE 都需要更新所有相关索引,索引越多,写操作的 I/O 和 CPU 开销越大。
  3. 存储成本增加:每个索引都需独立存储数据(InnoDB 中为 B+Tree 副本),占用磁盘空间,影响内存中 Buffer Pool 的有效数据缓存率。
  4. 优化器选择负担加重:过多索引可能导致优化器评估执行计划时间变长,甚至因统计信息不准确而选择非最优索引。

【深度推导/细节】

索引的“收益-成本”平衡模型

  • 收益函数:索引对查询的加速效果遵循“二八定律”,约 20% 的索引覆盖 80% 的高频查询,剩余索引收益很低。
  • 成本函数:
    • 写入放大效应:单行更新若涉及 N 个索引,实际需执行 N+1 次写入(数据页 + N 个索引页)。
    • 页分裂频率提升:索引过多时,随机插入易导致不同索引树的页分裂,增加碎片化和维护开销。
  • 临界点判断:当新增索引对查询的加速收益低于其带来的写入延迟和存储成本时,即达到“索引收益拐点”。

索引维护的隐藏成本

  1. Change Buffer 优化失效:对于非唯一二级索引,InnoDB 可使用 Change Buffer 延迟写入以减少随机 I/O,但索引过多会稀释该优化效果。
  2. 统计信息更新压力:ANALYZE TABLE 需扫描所有索引树来更新 cardinality,索引数量直接影响该操作的资源消耗和时间。
  3. 锁竞争加剧:索引拆分可能增加行锁升级为表锁的概率(尤其在低隔离级别下)。

【关联/对比】

与“索引覆盖”策略的关联

  • 理想情况:通过精心设计的复合索引实现“覆盖索引”(Using index),可避免回表查询,此时索引虽多但价值高。
  • 反面案例:大量单列索引或重复索引(如 (a,b) 和 (a) 同时存在)属于资源浪费,优化器通常无法合并使用。

与 NoSQL 系统的对比

  • MySQL:索引为“可选优化路径”,需手动权衡设计。
  • 某些 NoSQL(如 MongoDB):通过倒排索引、全文索引等机制,允许更多索引但采用异步更新策略,写入代价模型不同。

『面试官追问』

  1. “那到底建多少个索引算合适?有具体数字吗?”

    • 无绝对数字,但可参考原则:OLTP 系统通常建议单表不超过 5-7 个索引;宽表(列多)可略多,但需警惕索引列总数过多。
    • 量化标准:监控 Handler_write 状态变量,若索引更新占写入操作比例超过 30%,可能索引过多。
  2. “复合索引和多个单列索引怎么选?”

    • 复合索引优势:遵循最左前缀原则,一索引多用;可覆盖查询,避免回表;存储效率更高(前缀压缩)。
    • 单列索引适用场景:列独立作为查询条件频率高;列更新频繁,复合索引维护成本过高;需支持多列任意组合查询(但需评估是否值得)。
  3. “如何发现并删除无用索引?”

    • 使用 sys.schema_unused_indexes 视图(MySQL 5.7+)直接查看从未被使用的索引。
    • 开启 performance_schema,通过 events_statements_summary_by_digest 分析查询模式,识别未命中的索引。
    • 工具支持:pt-duplicate-key-checker 检测重复/冗余索引;pt-index-usage 分析慢日志中的索引使用情况。
  4. “索引对 JOIN 查询的影响是什么?”

    • 驱动表索引:用于加速 WHERE 条件过滤,减少嵌套循环次数。
    • 被驱动表索引:用于 JOIN 条件匹配(如外键列),避免全表扫描。
    • 风险点:多表 JOIN 时,优化器可能因索引统计信息偏差选择错误的驱动表顺序,反而降低性能。

【版本差异】

  • MySQL 5.6 之前:索引条件下推(ICP)未优化,过多索引可能导致大量无效回表。
  • MySQL 5.7+:支持生成列索引,可通过虚拟列对表达式建立索引,但需评估计算开销。
  • MySQL 8.0+:
    • 支持降序索引、函数索引,减少了为特殊排序需求创建冗余索引的必要。
    • 不可见索引特性允许将索引设置为“测试状态”,便于安全评估索引效果后再决定删除或保留。

【最佳实践建议】

  1. 设计阶段:遵循“基于查询模式设计索引”原则,使用 EXPLAIN 验证索引有效性。
  2. 监控阶段:定期检查索引使用率(INFORMATION_SCHEMA.INDEX_STATISTICS)和存储占比。
  3. 清理策略:优先删除重复索引、前缀重叠索引、低基数(cardinality < 5%)索引。
  4. 特殊场景例外:数据仓库中只读表可大量使用索引;归档表可删除非核心索引以节省空间。

结论重申:索引是“以空间换时间”的典型权衡,其数量应严格受控于实际查询需求,任何冗余索引都是潜在的性能负债而非资产。

My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# MySQL 中的事务隔离级别有哪些?

7 min read

事务隔离级别是数据库管理系统用于控制多个并发事务之间可见性和相互影响程度的一组标准规则,其本质是在数据一致性、并发性能和数据准确性三者之间进行权衡。

Read

Comments