八股:Mysql中 得索引数量是不是越多越好?为什么?

92 阅读4分钟

结论:

  • 不是越多越好,不论是从空间、时间、优化器选择、维护成本来说都不是越多越好。

原因

image.png

文字详解:

时间上:

  • 外部操作影响:
    • 每当我们对数据做增、删、改操作得时候,索引需要跟着对应得修改,当索引数量很多时,对应的索引列都要改,增加耗时,降低性能。
  • 内部变化影响:
    • 当删除或增加数据的时候,索引列会采用分裂或合并的方式,来进行调整,索引列很多的时候,大量的合并和分裂行为,也会增加耗时,降低性能。

空间上:

  • 空间占用大:

    • 每创建一个索引,就需要新增一个B+树(B+树每个节点,默认占用16KB),建立的索引很多的时候,会占用很多磁盘空间,影响性能。
  • 碎片化:

    • 当进行了大量的增删操作时,索引可能会因为需要进行分裂或合并,从而产生碎片化,增加IO次数,降低性能
    • 碎片化的索引还会导致更多的页缓存失效,进一步影响查询性能。(页缓存失效见扩展。)

优化器选择上:

  • 选择过多,耗时增加:

    • mysql在执行我们的SQL语句之前会使用优化器进行优化,优化器需要评估所有的索引,来选出最优选,如果索引过多,会增加选择的耗时
  • 错误选择:

    • 当索引过多的时候,优化器的选择会更加的复杂,从而可能导致优化器选择了错误的次优选,导致性能降低。
  • 放弃选择:

    • 在下述几种情况中,优化器会选择放弃使用索引,转而使用全表扫描

      • 评估不完: 索引过多,优化器评估方案耗时太久
      • 选择不出: 索引过多,优化器找不到一个明显优于其他索引的方案
      • 覆盖不足: 索引很多,但都不符合 多列的查询条件,没有合适的联合索引。
      • 选择性低: 当索引很多,但是索引的选择性很低,比如Boolean类型的列
      • 表的数据量小: 当在小表中,优化器发现使用全表扫描的效果比使用索引还好
      • 查询条件复杂: 在复杂查询,特别是多个子查询的时候,虽然后很多索引,但是使用索引的并不能显著提高性能,反而会增加表的复杂性
      • 索引竞争: 在索引很多,且高并发的情况下,多个查询条件,可能会竞争同一个索引,导致锁争用或者IO瓶颈(详解见扩展)

维护成本上:

  • 过多的索引,会增加数据库的维护成本,包括备份,恢复,迁移等。

扩展知识:

索引过多时,高并发场景下会出现锁争用和IO瓶颈,为什么?什么是锁争用?

  • 锁争用概念: 并发场景下,多个线程争夺一个锁,称为锁争用

  • InnoD中锁争用的体现:

    • InnoDB中对数据进行操作的时候,我们可以通过索引来快速定位到数据行。
    • 因此索引是查找到数据的关键结构。
    • 当我们在并发场景下,多个查询,并发访问同一个数据
    • 就需要并发访问同一个索引
    • 此时这种情况下,mysql就会对索引项加锁
    • 而只有拿到这把锁的查询,才可以继续访问数据
    • 所以这些并发查询就需要争抢这把锁,于是锁争用就出现了
  • InnoDB中锁争用的影响:

    • 并发事务中,InnoDB通过行级锁来保证事务的隔离
    • 当事务要对某一行数据进行改变的时候,会给当前行数据加上排他锁
    • 防止同一时间,有其他事务,对同一数据进行修改
    • 读取的时候,是共享锁
    • 增删改的时候,因为索引是查询到数据的关键
    • 所以也会对索引,添加排他锁
    • 当有新的索引被删除或者添加时
    • 索引页需要分裂或合并
    • 此时事务会给分裂出的新的索引页
    • 或要合并的相关的索引页
    • 也加上锁
    • 这就导致多个索引页同时被加锁
    • 降低了性能
    • 而索引越多,锁分裂和合并的也就越频繁,性能影响也就越大

IO瓶颈:

  • 合并和分裂影响:

    • 索引越多,修改数据时,需要合并和分裂的索引就越多
    • 合并和分裂越多,需要的IO操作就越多
  • 缓存失效影响:

    • 当索引越多,修改数据时,就需要频繁的修改缓存中的索引页
    • 也就是说缓存中的索引页会频繁的失效
    • 失效就需要更换新的
    • 更换新的就需要IO操作
  • 碎片化影响:

    • 当索引越多,修改数据的时候
    • 合并和分裂就越多
    • 产生碎片化的概率就越大
    • 索引碎片化的越多
    • 需要的IO操作就越多
    • 性能就越差