InnoDB关键特性-插入缓冲

一、什么是插入缓冲(Insert Buffer)

    在InnoDB存储引擎中,主键是行的唯一标识符。通常应用程序查询顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引一般是顺序的,不需要随机读取。如下按照下列SQL定义表。

create table t(
    a int auto_increment,
    b varchar(30),
    primary key(a),
    key (b)
);

    其中a列是你自增长的,对a列进行插入NULL,则会自动增长。同时页中的行记录也按照a的值进行顺序存放。一般情况下,不需要读取另一个页的记录。所以查询操作,速度是非常快的。

1.1 非聚簇索引插入带来的问题

    但是对于非聚簇索引b来说,插入一条数据并不保证b的值是递增的,插入的b值将会是一个随机值。
    1)这种情况,插入将需要离散访问非聚簇索引页,由于随机读取的存在导致了插入操作的性能下降;
    2)同时也会导致数据页分页的情况,导致额外的开销,插入就会变慢;

1.2 插入缓存如何解决此问题

    为了解决上述问题,对于非聚簇索引,InnoDB存储引擎设计了Insert Buffer。对于非聚簇索引的插入或者更新操作,不是每一次都插入到索引也中,而是先判断插入的非聚簇索引页是否在缓冲池中,如果在,则插入,否则,先放入一个Insert Buffer中。
    该非聚簇索引节点放入Insert Buffer后,告诉数据库这个索引已经插入到叶子节点中,实际上并没有插入,只是保存在另外一个位置。然后再已一定的频率和情况与非聚簇索引进行合并操作。此时,多条记录的多次插入操作合并到一次操作中,这就大大提高了非聚簇索引离散插入的性能。

二、插入缓冲触发的条件

    插入缓冲的使用,必须同时满足以下两个条件:
    1)索引必须是非聚簇索引;
    2)索引不是唯一索引;
    再进行大量的插入操作时,如果此时数据库宕机,这会导致非聚簇索引保存于Insert Buffer中的数据没有合并到非聚簇索引中。此时恢复可能会导致很长时间。

2.1 为什么不能是唯一索引

    不支持唯一索引,是因为如果非聚簇索引是唯一索引,那么在插入时需要校验唯一性,校验唯一性势必会发生离散读取的情况,又增加了开销,那么此时Insert Buffer失去了其原有的意义。

三、Change Buffer

    在InnoDB 1.0.x中引入了Change Buffer,可以理解为其是Insert Buffer的升级版本。Change Buffer对常见的DML语音都可以进行缓冲。包含insert、update、delete;对应着insert buffer,purge buffer, delete buffer。
    当然,change buffer的适用对象依然是非唯一的非聚簇索引。
    对于一条update的过程可以拆分为两个部分:
    1)第一个部分是将记录的delete_mask标记为删除
    2)第二个部分是真正的将记录删除。
    在innodb中,我们可以通过参数innodb_change_buffering来开启buffer的各种选项,该参数可选的值为inserts,deletes,purges,changes,all,none等,其中inserts,deletes和purges就是前面讨论过的情况,changes表示开启inserts和deletes,all表示开启所有。

四、Insert Buffer的实现

    Insert Buffer的数据结构,其实是一颗B+树,类似于聚簇索引,全局只有一颗Insert Buffer B+树,它负责对所有的表进行Insert Buffer记录,所以这颗B+树放在共享表空间中,也就是ibdata1文件中。因此,试图通过ibd文件恢复表数据的时候可能会出现check table失败,原因是表的辅助索引中的数据可能还在insert buffer中,所以通过ibd文件恢复文件之后,还需要进行repair table操作来重建表上的辅助索引。
Insert Buffer既然是一颗B+树,那么其必然存在叶子节点和非叶子节点。非叶子节点存放的是查询的search key值(用于定位该记录将要插入到哪一张表的哪一位置)。其构造如下:
在这里插入图片描述

    search key一共9个字节,space占4个字节,通过space用于标记其属于哪一张表。market占1个字节,用于兼容老版本的Insert Buffer。offset用于标示页所在的偏移量,占用4个字节。

4.1 Insert Buffer插入记录的过程

    当非聚簇索引要插入记录到页中时,如果这个页不在缓冲池时,那么InnoDB存储引擎会根据上述规则构造一个search key,接下来查询Insert Buffer这颗B+树,然后将这条记录插入到Insert Buffer B+树中的叶子节点中。对于插入的数据,并非将search key简单插入,而是按照如下规则重新构造一条记录插入到叶子节点。
在这里插入图片描述

    可以发现,叶子节点保存的记录,除了space,marker,offset之外,还多个metadata和另外4个字段,因此和单纯的数据记录相比,Insert Buffer还需要额外的13个字节的开销。
    1)matadata字段,占用4个字节,用来记录此条信息插入Insert Buffer的顺序。
    2)从第五列开始,就是实际插入的各个字段(非聚簇索引的值)的值了。

4.2 Insert Buffer Bitmap的作用

    为了保证Insert Buffer中的数据每次都能够成功合并到索引页中,此时需要一个特殊的数据页来标记非聚簇索引页的可用空间大小。该数据页类型为Insert Buffer Bitmap。1个Insert Buffer Bitmap页可以追踪16384个非聚簇索引页(space, page_no)的可用空间。每个非聚簇索引页在Insert Buffer Bitmap页中占用4个字节。由以下三个部分组成。
在这里插入图片描述

五、Merge Insert Buffer

    通过前面小节可知道Insert Buffer/Change Buffer是一颗B+树。若需要插入的非聚簇索引不在缓冲池中,那么该条非聚簇索引记录将会首先插入到Insert Buffer/Change Buffer这颗B+树中。那么插入之后,何时将Insert Buffer中的数据合并到真正的非聚簇索引页中呢?

5.1 索引页被读取到缓存池时

    当非聚簇索引被读取到缓冲池中时,如正常执行的SELECT操作,这是需要检查Insert Buffer Bitmap页,然后确认该辅助索引是否有记录存放在Insert Buffer B+树中。若有,则将Insert Buffer B+树中的该页的记录插入到索引页中。可以看到对该页的多次的操作记录可以通过一次合并操作合并到了索引页中,因此性能大幅提高。

5.2 索引页的可用空间小于1/32

    Insert Buufer Bitmap页用于追踪每个索引页的可用空间大小,并至少还有1/32的可用空间。如果插入索引页记录时检测到插入记录后可用空间会小于1/32也,则会进行一个合并操作,即前置读取非聚簇索引页,将Insert Buffer B+树中该页的记录合并到索引页中。

5.3 Master Thread每秒执行一次的Merge Insert Buffer操作

    在Insert Buffer B+树中,索引页都是根据(space,offset)排好序的。每次合并时,InnoDB存储引擎通过随机选择Insert Buffer B+树的一个页,读取该space以及之后所需要数量的页进行合并操作。该算法是为了保证在复杂的情况下具有更好的公平性。
如果进行Merge时,要进行Merge的表已经被删除,此时直接丢弃Insert Buffer/Change Buffer中的数据记录。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值