Innodb插入缓存

173 阅读4分钟

在innodb当中,除了缓存池之外,还有一种缓存,就是插入缓存,尽管名字上带有缓存,但实际上和数据页一样,也是物理页的一个组成部分,它是Innodb的关键特征之一,那么今天我们以小白的思路来看一下。

基本概念

页(page)

页是innodb磁盘管理最小的单位,innodb每个页的大小是16K,且不可更改。常见的类型有:数据页 B-tree Node;undo页 Undo Log Page;系统页 System Page;事务数据页 Transaction system Page;插入缓冲位图页 Insert Buffer Bitmap;插入缓冲空闲列表页 Insert Buffer freeBitmap;未压缩的二进制大对象页Uncompressed BLOB Page;压缩的二进制大对象页 Compressed BLOB Page。

区(extend)

区是由64个连续的页主成,每个页大小为16K,即每个区的大小为(64*16K)=1MB,对于大的数据段,mysql每次最多可以申请4个区,以此保证数据的顺序性能。

段(segment)

常见的segment有数据段、索引段、回滚段。innodb是索引聚集表,所以数据就是索引,索引就是数据,那么数据段即是B+树的页节点(leaf node segment),索引段即为B+树的非索引节点(non-leaf node segment)。而且段的管理是由引擎本身完成的。

随机读取

为啥要说上面这些呢,应为想解释innodb的预读机制,InnoDB使用两种预读算法来提高I/O性能::

线性预读(linear read-ahead)

如果一个区中的被顺序读取的页超过或者等于 innodb_read_ahead_threshold参数变量时,Innodb将会异步的将下一个区读取到缓存池当中。

随机预读(randomread-ahead)

随机预读方式则是表示当同一个区中的一些页在缓存池中发现时,Innodb会将这个区中的剩余页一并读到buffer pool中,由于随机预读方式给Innodb code带来了一些不必要的复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃。要启用此功能,请将配置变量设置innodb_random_read_ahead为ON。

插入缓存

首先来聊聊为啥要有插入缓存,之前说缓存池的时候,好像主要针对的是查询,那么这里开始好好琢磨一下插入,一般情况下,开发者在定义数据表的时候,会有一个主键自增id(也就是主键索引):

create table student(id int primary key auto_increment, s_name varchar(20).....);

这样的情况下,Innodb的插入是很方便的,id的是唯一且自增的,插入顺序按照主键递增的顺序进行插入,插入聚集索引(parmary key)也是顺序的,不需要随机读取磁盘,这种情况下的插入是很快的,但是这里也要注意,不见得id就是自增的,比如uuid,或者直接对id进行插入,导致id不连续都可能导致随机读取磁盘(也就是随机预读)。

但是很多时候,表当中不可能只有聚集索引,更多的情况下是拥有多个非聚集辅助索引,比如,我们按照学员的姓名去查找学员,那么对姓名这个字段设置了索引。

create table student(id int primary key auto_increment, s_name varchar(20),key(s_name));

那么这个时候,s_name就是非聚集索引了,在插入s_name的时候就需要离散的访问非聚集索引页,由于随机读取的存在导致了插入操作的性能下降,这个和s_name采用索引的关系不大,是由于b+树特性导致的。

那么这种情况下insert buffer就起到了作用,因为非聚集型索引在插入的时候需要进行随机读入,所以导致性能下降,那么insert buffer的策略就是,不要每次都直接将非聚集索引插入到索引页当中,而是,首先查看非聚集索引是否在缓存池当中,如果在,那么直接插入,如果不在,存放到insert buffer当中,然后以一定的频率去和索引页字节的进行合并(merge),这样的好处就是把多个插入合并到一个操作当中,而且针对的是同一个索引页,所以大大的提高了非聚集索引的插入效率。

所以insert buffer解决的是非聚集索引的插入效率问题,要使用insert buffer需要具备下面的两个条件:

1、索引是辅助索引

2、索引不是唯一的

好了,关于插入缓存的理解就先聊到这里,之后我们还会详细的聊聊插入缓存的实现原理。还是请各位大佬多多指点。