您的位置:首页 > 数据库 > MySQL

InnoDB存储引擎——插入缓冲

2017-03-13 17:27 357 查看
InnoDB存储引擎的关键特性包括:

插入缓冲(Insert Buffer)

两次写(Double Write)

自适应哈希索引(Adaptive Hash Index)

异步IO(Async IO)

刷新邻接页(Flush Neighbor Page)

1、Insert Buffer

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

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


其中a列是自增长的,若对a列插入NULL值,则由于其具有auto_increment属性,其值会自动增长。同时页中的行记录按a的值进行顺序存放。

在一般情况下,不需要随机读取另一个页中的记录。因此,对于这类情况下的插入操作,速度是非常快的。

一般情况下,一张表上有多个非聚集的辅助索引。如果表中有一个主键a,同时也有非聚集的且不是唯一的索引,那么在进行插入操作时,数据页的存放还是按主键a进行顺序存放的,但是对于非聚集索引叶子节点的插入不再是顺序的,由于随机读取的存在而导致了插入性能的下降。

InnoDB存储引擎中的Insert Buffer,对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中。然后再以一定的频率进行Insert Buffer与辅助索引页子节点的合并操作,这时通常能将多个插入合并到一个操作中,这就大大提高了对于非聚集索引插入的性能。

Insert Buffer的使用需要同时满足以下两个条件:

索引是辅助索引;

索引不是唯一的;

当满足以上两个条件时,InnoDB存储引擎会使用Insert Buffer,这样就提高了插入的性能。

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

可以通过下面的命令来查看插入缓冲的信息:

mysql> show engine innodb status\G

下面是部分输出:

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 276671, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s


seg size显示了当前的Insert Buffer的大小为2*16KB,为32KB;

free list len代表了空闲列表的长度;

size代表了已经合并记录页的数量;

Insert Buffer目前存在的一个问题是:在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认最大可以占用到1/2的缓冲池内存。

下面是InnoDB源代码中对于insert buffer的初始化操作:

#define IBUF_POLL_SIZE_PER_MAX_SIZE 2
ibuf->max_size = buf_pool_get_curr_size()/UNIV_PAGE_SIZE/IBUF_POLL_SIZE_PER_MAX_SIZE


修改IBUF_POLL_SIZE_PER_MAX_SIZE可以对插入缓冲的大小进行控制。比如将其改为3,则最大只能使用1/3的缓冲池内存;

2、Change Buffer

InnoDB从1.0.x版本开始引入了Change Buffer,可以将其视为Insert Buffer的升级。从这个版本开始,InnoDB可以对DML操作——Insert、Delete、Update都进行缓冲,它们分别是:Insert Buffer, Delete Buffer,Purge Buffer。

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

对一条记录进行Update操作可能分为两个过程:

1)将记录标记为已删除;

2)真正将记录删除;

因此,Delete Buffer对应的Update操作的第一个过程,即将记录标记为删除。

Purge Buffer对应Update操作的第二个过程,即将记录真正的删除。

同时,InnoDB提供了参数innodb_change_buffering,用来开启各种Buffer的选项。该参数可选的值为:inserts,deletes,purges,changes,all,none。

inserts,deletes,purges就是前面讨论的三种情况。

changes表示启动inserts和deletes,all表示启动所有,none表示都不启动。该参数默认值为all。

mysql> show variables like 'innodb_change_buffering';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_change_buffering | all   |
+-------------------------+-------+
1 row in set (0.00 sec)


从InnoDB 1.2.x版本开始,可以通过参数innodb_change_buffer_max_size来控制Change Buffer最大使用内存的数量,默认值为25,表示最多使用1/4的缓冲池内存控件。该参数的最大有效值为50;

mysql> show engine innodb status\G


下面是部分输出:

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 276671, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s


这里可以看到merged operation和discarded operation,并且下面具体显示Change Buffer中每个操作的次数,insert表示Insert Buffer;delete mark表示Delete Buffer;delete表示Purge Buffer;

discarded operations表示当Change Buffer发生merge时,表已经被删除,此时就无需再将记录合并(merge)到辅助索引中了。

3、Insert Buffer的内部实现

之前了解了Insert Buffer是什么,也知道了它的使用场景是针对非唯一辅助索引的插入操作。

Insert Buffer的数据结构是一棵B+树。

在MySql 4.1之前的版本中每张表有一棵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表示待插入记录所在表的表空间id,在InnoDB存储引擎中,每个表有一个唯一的space id,可以通过space id查询得知是哪张表。space 占用4字节。marker占1个字节,它是用来兼容老版本的Insert Buffer。offset表示页所在的偏移量,占用4个字节。

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

对于插入到Insert Buffer B+树叶子节点的记录,并不是直接将待插入的记录插入,而是需要根据如下的规则进行构造:



space,marker,offset和之前的意思一样,共9个字节。

metadata占用4个字节,其存储的内容如下所示:



IBUF_REC_OFFSET_COUNT是保存两个字节的整数,用来排序每个记录进入Insert Buffer的顺序。

后面的就是实际插入记录的各个字段的内容,所以,Insert Buffer B+树的叶子节点记录需要额外13字节的开销。

启动Insert Buffer索引后,辅助索引页(space,offset)中的记录可能被插入到Insert Buffer B+树中,所以为了保证每次Merge Insert Buffer页必须成功,还需要有一个特殊的页用来标记每个辅助索引页(space,offset)的可用空间。这个页类型为Insert Buffer Bitmap;

每个Insert Buffer Bitmap页用来追踪16384个辅助索引页,也就是256个区(Extent)。

每个辅助索引页在Insert Buffer Bitmap页中占用4bit,各个位的含义如下:



4、Merge Insert Buffer

Insert/Change Buffer是一棵B+树。若需要实现插入记录的辅助索引页不在缓冲池中,那么需要将辅助索引记录首先插入到这棵B+树中。但是Insert Buffer中的记录何时合并(Merge)到真正的辅助索引中?

Merge Insert Buffer的操作可能发生在以下几种情况:

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

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

3)Master Thread;

当辅助索引页被读取到缓冲池中时,比如当执行select操作时,这时需要检查Insert Buffer Bitmap页,然后确认该辅助索引页是否有记录存放于Insert Buffer B+树中。若有,则将Insert Buffer B+树中该页的记录插入到该辅助索引页中。

Insert Buffer Bitmap页追踪每个辅助索引页的可用空间,并至少有1/32页的空间。若插入辅助索引记录时检测到插入记录后可用空间会小于1/32页,则会强制进行一次合并操作,即强制读取辅助索引页,将Insert Buffer B+树中该页的记录插入到该辅助索引页中。

Master Thread线程中每秒或每10秒会进行一次Merge Insert Buffer的操作,不同之处在于每次进行merge操作的页的数量不同。

在Master Thread中,执行merge操作的不止是一个页,而是根据srv_innodb_io_capacity的百分比来决定真正要合并多少个辅助索引页。但InnoDB存储引擎又是根据什么要合并的辅助索引页呢?

在Insert Buffer B+树中,辅助索引页根据(space,offset)已经排好序了,可以根据(space,offset)的排序顺序进行页的选择。

然而,Insert Buffer页的选择,InnoDB不是采用这个方式,它随机地选择Insert Buffer B+树的一个页,读取该页中的space及之后所需要数量的页。

若进行merge时 ,要进行merge的表已经被删除,此时可以直接丢弃已经被Insert/Change Buffer的数据记录;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  mysql InnoDB 插入缓冲