什么是插入缓冲(Insert Buffer)?
一般情况下应用程序中主键是唯一且自增的,数据是按照主键递增的顺序进行插入的,所以插入聚集索引一般都是顺序的,不需要磁盘的随机读取。对于这种插入操作,速度是非常快的。如果主键是UUID这种非自增的方式,那么插入方式和辅助索引一样都是随机的。
辅助索引是非顺序的插入,需要离散地访问辅助索引页,由于随机读取磁盘所以会导致插入性能的下降。
为了解决上述问题,InnoDB存储引擎设计了插入缓冲(Insert Buffer)。辅助索引的插入操作,会先判断插入的辅助索引页是否在缓冲池中,若在则直接插入;否则先放到插入缓冲中,然后以一定的频率和情况进行Insert Buffer和辅助索引叶子节点的合并操作,通过将多个插入合并到一个操作中(因为在一个索引页中),大大提高了对于辅助索引的插入性能。
只有满足以下两个条件,InnoDB存储引擎才会使用Insert Buffer,从而提高插入操作的性能。
- 索引是辅助索引
- 索引不是唯一的
注意:
- 当进行大量插入操作,并使用了Insert Buffer,如果此时数据库发生宕机,这时会有大量的Insert Buffer没有合并到实际的辅助索引中。所以这时可能会需要很长时间恢复。
- 辅助索引不可以是唯一的,因为如果是唯一的话,需要判断插入记录的唯一性,造成离散读取的情况发生,从而导致Insert Buffer失去了意义。
- 在写入密集情况下,插入缓冲会占用过多的缓冲池内存,默认最大可占用1/2,这样会对其他操作带来一定的影响。我们可以通过IBUF_POOL_SIZE_PER_MAX_SIZE对缓冲池大小进行控制,值为3,则最大能使用1/3的缓冲池内存。
Change Buffer
change Buffer是MySQL5.5加入的新特性,是insert buffer的加强,insert buffer只针对insert有效,change Buffer对insert、delete、update(delete+insert)、purge都有效。当修改辅助索引时,如果不在缓冲池中,修改信息就会被放在change buffer中,当通过索引查询需要将索引读取到缓冲池时,会和change buffer中修改信息合并,再一定频率刷新回磁盘。
目的是为了减少随机IO带来性能损耗,说白了,就是把随机IO尽量变成顺序IO。
插入缓冲的内部实现
Insert Buffer的数据结构是一颗B+树,MySQL4.1版本之后全局只有一个Insert Buffer B+树,负责对所有表的辅助索引进行Insert Buffer。这棵树是放在共享表空间中,默认是ibdata1中。
Insert Buffer B+树数据结构:
非叶子节点存放的是查询键值(search key),如下图:
字段 | 长度 | 含义 |
space | 4 | 表空间id,可以查询到需要插入哪张表。 |
marker | 1 | 用来兼容老版本Insert Buffer |
offset | 4 | 表示页所在的偏移量 |
叶子节点如下图:
字段 | 长度 | 含义 |
space | 4 | 表空间id,可以查询到需要插入哪张表。 |
marker | 1 | 用来兼容老版本Insert Buffer |
offset | 4 | 表示页所在的偏移量 |
metadata | 4 | 略。 |
secondary index record | 实际插入记录从该字段开始存储 |
何时插入到真正的辅助索引中?
- 辅助索引页被读取到缓冲池时
当执行正常的SELECT查询操作,这时需要检查Insert Buffer Bitmap页,然后确认该辅助索引页是否是有记录存放在Insert Buffer B+树中。若有,则将Insert Buffer B+树中该页记录插入到该辅助索引页中。
- Insert Buffer Bitmap页追踪到该辅助索引页无可用空间时
Insert Buffer Bitmap页是用来追踪每个辅助索引页的可用空间,当检测到插入记录后可用空间小于1/32页,则强制进行合并操作。
- Master Thread
Master Thread线程每秒或每10秒会进行一次Merge Insert Buffer的操作,本此的合并操作是根据srv_innodb_io_capactiy的百分比来决定真正要合并多少哥辅助索引页。InnoDB存储引擎是采用随机选择Insert Buffer B+树的一个页,读取该页中的space及之后所需要数量的页(该算法的好处是,在复杂情况下具有更好的公平性)。当进行合并时,其中表已经被删除,此时可以直接丢弃Insert/Change Buffer中的数据记录。