[InnoDB 学习1-6] 插入缓冲

 

插入缓冲(Insert Buffer)

Insert Buffer

作用

背景:

通常应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引(Primary Key)一般是顺序的,不需要磁盘的随机读取。

但并不是所有的主键都是顺序的,例如UUID。那么插入和辅助索引一样,同样是随机的。即使主键是自增类型,但插入的是指定的值,而不是NULL值(手动指定主键值),那么同样可能导致插入非连续的情况。

不可能每张表上只有一个聚集索引,更多情况下,一张表上有多个非聚集的辅助索引(secondary index)。(比如,用户需要按照name字段进行查找,且这个字段不是唯一的)

这种情况下,产生了一个非聚集的且不是唯一的索引。在进行插入操作时,数据页的存放还是按主键进行顺序存放,但是对于非聚集索引叶子节点的插入不再是顺序的了。这时就需要离散地访问非聚集索引页,由于随机读取的存放而导致了插入操作性能下降。这是由于B+树的特性决定了非聚集索引插入的离散性。

某些情况下,辅助索引的插入也是顺序的,如时间。

解决:

InnoDB存储引擎开创行地设计了Insert Buffer对于非聚集索引的插入/更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中。若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中。

类似欺骗,数据库这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge(合并)操作。

因为,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。

使用条件

需要同时满足2个条件:

  • 索引是辅助索引(secondary index)。
  • 索引不是唯一的(unique)。

其实,条件与主键相对。

为什么辅助索引不能是唯一的?

因为在插入缓冲时,数据库并不去查找索引页来判断插入的记录的唯一性。如果去查找肯定又会有离散读取的情况发生,从而导致Insert Buffer失去了意义。

Change Buffer

Change Buffer可视为Insert Buffer的升级。有了这个后的新版本,可以对DML操作:INSERT、DELETE、UPDATE都进行缓冲。分别是:Insert Buffer、Delete Buffer、Purge Buffer。

注:和Insert Buffer一样,Change Buffer适用的对象依然是非唯一的辅助索引

对一条记录进行UPDATE操作分为2步:

  • 将记录标记为已删除。
  • 真正将记录删除。

Delete Buffer对应UPDATE操作的第一个过程。

Purge Buffer对应UPDATE操作的第二个过程。

mysql> show variables like 'innodb_change_buffer_max_size';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
+-------------------------------+-------+

25%表示Change Buffer使用最多1/4的缓冲池内存空间。

Insert Buffer的内部实现

Insert Buffer的数据结构是一棵B+树。(全局只有一棵,以前是每张表有一棵)

这棵B+树存放在共享表空间中,默认就是ibdata1中。

因此,试图通过独立表空间ibd文件恢复表中数据时,往往会导致CHECK TABLE失败。这是因为表的辅助索引中的数据可能还在Insert Buffer中,也就是共享表空间中。所以通过ibd文件进行恢复后,还需要进行REPAIR TABLE操作来重建表上所有的辅助索引。

Insert Buffer是一棵B+树,因此其也由叶节点和非叶节点组成。非叶节点存放的是查询的search key(键值)。

(search key结构如下)

  • space marker offset

search key一共占用9个字节,其中space(占4个字节)表示待插入记录所在表的表空间id。在InnoDB存储引擎中,每个表有一个唯一的space id,可以通过space id查询得知是哪张表。market(占1个字节)用来兼容老版本的Insert Buffer。offset(占4个字节)表示页所在的偏移量。

当一个辅助索引要插入到页(space, offset)时,如果这个页不在缓冲池中,那么InnoDB存储引擎首先根据上述规则构造一个search key,接下来查询Insert Buffer这棵B+树,然后再将这条记录插入到Insert Buffer B+树的叶子节点中。

对于叶节点的记录的插入,需要进行一定的规则进行构造。

  • space marker offset metadata        

metadata:记录每条记录进入Insert Buffer的顺序等。

额外还有Insert Buffer Bitmap页,用来追踪16384个辅助索引页,也就是256个区(Extent)。每个Insert Buffer Bitmap页都在16384个页的第二个页中。

用于:

  • 记录该辅助索引页的可用空间数量;
  • 记录该辅助索引页是否有记录缓存在Insert Buffer B+树中;
  • 该页是否为Insert Buffer B+树的索引页。

Merge Insert Buffer

Insert Buffer中的记录是何时合并(merge)到真正的辅助索引中的呢?

  • 辅助索引页被读取到缓冲池时。

    例如:这在执行正常的SELECT查询操作,此时需要检查Insert Buffer Bitmap页,然后确认该辅助索引页是否有记录存放于Insert Buffer B+树中。若有,则将Insert Buffer B+树中该页的记录插入到该辅助索引页中。

  • Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时。
  • Master Thread。

Master Thread中,是根据怎样的算法得知需要合并的辅助索引页?

随机选择Insert Buffer B+树的一个页。该算法在复杂情况下,有更好的公平性。