InnoDB关键特性

InnoDB关键特性

InnoDB存储引擎的关键特性包括:

  • 1.插入缓冲(InsertBuffer)
  • 2.两次写(DoubleWrite)
  • 3.自适应哈希索引(AdaptiveHash Index)
  • 4.异步IO(Async IO)
  • 5.刷新邻接页(FlushNeighbor Page)

1 插入缓冲(Insert Buffer)

Insert Buffer 可能是InnoDB存储引擎关键特性中最令人激动与兴奋的一个功能。不过这个名字可能会让人认为插入缓冲是缓冲池中的一个组成部分。 其实不然,lnnoDB缓冲池中有 Insert Buffer信息固然不错,但是Insert Buffer和数据页一样,也是物理页的一个组成部分。

在InnoDB存储引擎中,主键是行唯一的标识符。通常应用程序中行记录的插入顺序是按照主键递增的顺序进行。因此,插入聚集索引(primay key)一般是顺序的,不需要磁盘的随机读取。

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

其中a列是自增长的,若对a列插入null值,则由于其具有auto_increment属性,其值会自动增长。同时页中的行记录按a的值进行顺序存放。在一般情况下,不需要随机读取另一个页中的记录。因此,对于这类情况下的插入操作,速度是非常快的。

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

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

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

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

需要注意的是,在某些情况下,辅助索引的插人依然是顺序的,或者说是比较顺序的,比如用户购买表中的时间字段。在通常情况下,用户购买时间是一个辅助索引,用来根据时间条件进行查询。但是在插入时根据时间的递增而插入的,因此插入也是“较为”顺序的。

InnoDB存储引擎开创性地设计了Insert Buffer, 对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中,好似欺骗。数据库这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge (合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。

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

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

当满足以上两个条件时,InnoDB 存储引擎会使用Insert Buffer, 这样就能提高插入操作的性能了。不过考虑这样一种情况:应用程序进行大量的插入操作,这些都涉及了不唯一的非聚集索引,也就是使用了Insert Buffer。若此时MySQL数据库发生了宕机,这时势必有大量的Insert Buffer并没有合并到实际的非聚集索引中去。因此这时恢复可能需要很长的时间,在极端情况下甚至需要几个小时。

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

用户可以通过命令SHOW ENGINE INNODB STATUS 来查看插入缓冲的信息:

mysql> show engine innodb status\G
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
123 inserts, 112 merged recs, 30 merges

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

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

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

merges代表合并的次数,也就是实际读取页的次数;

merged Insert代表了插入的记录数;

merges:merged Insert大约为1:3,代表了插入缓冲将对于非聚集索引页的离散IO逻辑请求大约降低了2\3。

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

 

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。

从InnoDB 1.2.x版本开始,可以通过参数innodb_change_buffer_max_size来控制 Change Buffer最大使用内存的数量。

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

innodb_change_buffer_max_size值 默认为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

merged operations 表示change buffer中每个操作的次数。
insert表示Insert Buffer;

delete mark表示Delete Buffer;

delete表示Purge Buffer;

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

posted @ 2019-03-20 23:10  DB小小白  阅读(186)  评论(0)    收藏  举报