我们都知道MySql有一个缓存池,但是insert buffer与缓存池不太一样,insert buffer也是物理页的一个组成部分(insert buffer属于缓存池的一部分)。

聚集索引(primary key)一般是顺序的,插入的时候不需要随机读取,但是对于辅助索引(非聚集索引),其叶子节点的插入不是顺序的(因为非B+树的特性,导致非顺序的索引插入是离散的),需要离散地读取非聚集索引页,由于随机读取而导致了插入性能下降,如果每次插入都直接更新索引,对性能的影响更大。因此,InnoDB引入了Insert Buffer,对于非聚集索引的插入和更新操作,先判断是否存在与缓冲池中,若存在则直接插入,否则先放到insert buffer中,再以一定的频率merge到真正的索引中,并更新到缓冲池(真正将非聚集索引插入到B+树的叶子节点)。


有可能是mysql 配置的key_buffer_size mysql key buffer_缓存

Insert Buffer存在的缺点:

  1. 如果大量插入了非聚集索引,会先放到Insert buffer中,如果发生了宕机,那么恢复索引可能需要很长的时间
  2. 可能在Insert buffer中存了重复的辅助索引,因为插入的时候不会查索引页是否存在该索引,否则就失去了辅助索引的意义了(查索引需要离散读取)

Insert Buffer可以占用的内存:

可以通过IBUF_POOL_SIZE_PER_MAX_SIZE控制Insert Buffer占缓存池内存的大小,如设置为3,表示最大可占用缓存池1/3的内存。(对于宕机恢复时间长的问题,可以适当减少Insert Buffer的内存)

Insert Buffer的实现:

Insert Buffer是一颗B+树,存放于共享空间的ibdata1文件中(通过独立表空间恢复数据之后还需要通过REPAIR TABLE 来恢复非聚集索引)

Insert Buffer的B+树非叶子节点中存放的是查询的search key。space 占4个字节,存了InnoDB表的一个唯一space id;marker 占1个字节,用于兼容老版本的Insert Buffer;offset 占4个字节,表示表页所在的偏移量。search key表示插入的索引所在的页为(space, offset)。


有可能是mysql 配置的key_buffer_size mysql key buffer_辅助索引_02

当需要插入的辅助索引不在缓存池时,会构建一个新的search key,然后查询Insert Buffer这颗B+树,再将这条记录插入到Insert Buffer B+树的叶子节点中。叶子节点的结构如下,前三个数据与search key一致。metadata占4个字节:IBUF_REC_OFFSET_COUNT为2个字节的整数,用于排序每个记录进入Insert Buffer的顺序;IBUF_REC_OFFSET_TYPE占1个字节;IBUF_REC_OFFSET_FLAG占1个字节。

从叶子节点的第五个字段开始就是这条记录的各个数据了(metadate之后)。


有可能是mysql 配置的key_buffer_size mysql key buffer_辅助索引_03

**Insert Buffer BitMap:**追踪辅助索引页


有可能是mysql 配置的key_buffer_size mysql key buffer_非聚集索引_04

Insert Buffer 合并到索引:

发生Merge Insert Buffer的三种情况:

  1. 辅助索引页被读取到缓存池。例如执行select操作时,会先检查Insert Buffer中有没有相关的辅助索引,若有则插入到辅助索引页中。
  2. Insert Buffer BitMap 追踪到该辅助索引页已无可用空间。
  3. Master Thread 每秒或每10秒进行一次合并操作