Mysql Insert buffer

  在InnoDB中,主键是行的唯一标识符,因此插入聚集索引(primary key)一般是顺序的,不需要磁盘随机读取,对于这类插入速度是最快的。但并不是所有主键插入都是顺序的,若主键是UUID这类数据,插入就和辅助索引一样是随机的了。为了提高随机插入的性能,InnoDB设计了Insert buffer,虽然从名字看起来很像内存的组成部分,实际上,Insert buffer和数据页一样,是物理页的一个组成部分,它保存在共享表空间中。

  当一个表中除了聚集索引外,还有多个辅助索引(secondary index),这时产生了非聚集不唯一索引,在进行插入操作时,数据页还是按主键进行顺序存放,但对于辅助索引叶子节点的插入不再是顺序的了,这时需要离散的访问非聚集索引页,这种随机读取导致插入操作性能下降,这是B+树的特性决定了非聚集索引插入的离散性。

  开启了insert buffer之后,对于非聚集索引的插入或者更新操作,不是每一次直接插入到索引页,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入,否则先插入到一个insert buffer对象中。然后以一定频率和情况进行insert buffer和辅助索引叶子节点的merge(合并)操作,这时通常能将多个插入合并到一个操作中,这样大大提高了非聚集索引的插入性能。

  当然,insert buffer的使用也是需要满足一定条件的:

  1.索引必须是辅助索引  2.索引必须是不唯一的

  只有同时满足这两个条件的前提下,InnoDB才会使用insert buffer对象。当然,凡事都有两面性,使用了insert buffer也导致一个问题的出现:如果数据库意外宕机,这时如果有大量的insert buffer还没有合并到实际的索引页中去,恢复的时间就会很长。

  从InnoDB1.0.x开始引入了Change buffer,可以看作是insert buffer的升级。这个版本之后,InnoDB可以对insert,delete和update这些DML操作都进行缓冲,分别对应insert buffer,delete buffer和purge buffer。当然,change buffer适用条件依然是是非唯一辅助索引。其中update分为两个过程:将记录标记为已删除(delete buffer),然后真正将记录删除(purge buffer)。从InnoDB1.2.x开始通过innodb_change_buffer_max_size控制change buffer的最大使用内存数量,默认值是25,表示最多使用缓冲池1/4的空间,该值最大有效值为50。

 

Insert buffer内部实现:

  Insert buffer的数据结构是一棵B+树,在MySQL4.1之前,每张表都有一棵Insert Buffer B+树。但是之后,全局只有一棵Insert Buffer B+树,存放在共享表空间中,负责对所有表的辅助索引进行insert buffer。因此,通过独立表空间ibd文件恢复表中数据时,还需要通过repair table操作来重建表上的所有辅助索引,否则check table会失败,因为表的辅助索引中的数据可能还在insert buffer中,也就是共享表空间中。

  insert buffer是一棵B+树,所以它也是有叶节点和非叶节点组成的。非叶节点存放的是查询的search key,由如下三部分组成:

  

  search key一共占9个字节,其中space表示待插入记录所在表的表空间id,占4字节,marker占1字节,用来兼容老版本insert buffer。offset占4字节,表示页所在的偏移量,当一个辅助索引要插入到页(space_id,offset)时,如果这个页不在缓冲池中,那么InnoDB首先根据上述规则构造一个search key,然后将这条记录插入到insert buffer B+树对应位置的叶子节点中。

   叶子节点记录:

  

  对于待插入记录需要按照上面规则进行构造,而不是直接插入。其中space,marker,offset和非叶节点含义相同,共占9字节。metadata占4字节,由三部分组成,IBUF_REC_OFFSET_COUNT、IBUF_REC_OFFSET_TYPE、IBUF_REC_OFFSET_FLAGS,分别占2字节、1字节、1字节。IBUF_REC_OFFSET_COUNT对每个进入Insert Buffer的记录进行排序。

  启用了Insert Buffer索引后,辅助索引页中的记录可能被插入到Insert Buffer B+树中,为保证每次merge insert buffer页必须成功,需要一个特殊页来标记每个辅助索引页的可用空间,这个页类型为Insert Buffer Bitmap。每个Insert Buffer Bitmap页可以追踪16384个辅助索引页,它在16384个页的第二个页中。每个辅助索引页在Insert Buffer Bitmap页中占4位,结构如下:

  

  Insert buffer合并发生的几种情况:

  1)辅助索引页被读取到缓冲池时

  2)Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时

  3)Master Thread的合并操作

  第一种情况为当辅助索引被读入缓冲池时,需要检查Insert Buffer Bitmap页,确认该辅助索引页是否有记录存放在Insert Buffer B+树中,如果有,则将Insert Buffer B+树中该页的所有记录一次性合并到该辅助索引页中,因此性能会有大幅提高。

  第二种情况,当插入辅助索引记录时检测到插入记录后可用空间会小于该辅助索引页的1/32时,会强制进行一次合并操作,将Insert Buffer B+树中该页的记录及待插入记录都插入到该辅助索引页中。

  第三种情况,Master Thread线程中每秒或每10秒会进行一次Merge Insert Buffer操作,不同的是,在Master Thread中进行的merge操作是根据srv_innodb_io_capacity的百分比决定要合并的辅助索引页数量。在进行merge时,如果进行merge的表已经被删除,此时可以直接丢弃已经被Insert/Change Buffer的数据记录。

 

通过show engine innodb status可以查看Insert Buffer的相关信息:

Ibuf:size 7545,free list len 3790,seg size 11336,

63523345 inserts,2534532 merged recs,223234 merges

。。。

  seg size显示了当前insert buffer大小,11336 x 16KB,free list len代表空闲列表长度;size 代表已经合并记录的数量。第二行才是用户真正关心的,它显示了插入性能。inserts 代表插入的记录数,merged recs代表合并的插入记录数量;merges代表合并的次数,也就是实际读取页的次数。merges:merged recs 大约1:3,代表了插入缓冲将对于非聚集索引页的离散IO逻辑请求大约降低了2/3.

 

posted on 2018-12-19 18:16  MrLeo701  阅读(1563)  评论(0编辑  收藏  举报

导航