插入缓冲(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+树的一个页。该算法在复杂情况下,有更好的公平性。