innodb 插入缓冲

插入缓冲是InnoDB存储引擎关键特性中最令人激动的。不过,这个名字可能会让人认为插入缓冲是缓冲池中的一个部分。其实不然,InnoDB缓冲池中有Insert Buffer信息固然不错,但是Insert Buffer和数据页一样,也是物理页的一个组成部分。
我们知道,主键是行唯一的标识符,在应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引一般是顺序的,不需要磁盘的随机读取。比如说我们按下列SQL定义的表。
mysql> create table t ( id int auto_increment, name varchar(30),primary key (id));
Query OK, 0 rows affected (0.14 sec)
id列是自增长的,这意味着当执行插入操作时,id列会自动增长,页中的行记录按id执行顺序存放。一般情况下,不需要随机读取另一页执行记录的存放。因此,在这样的情况下,插入操作一般很快就能完成。但是,不可能每张表上只有一个聚集索引,在更多的情况下,一张表上有多个非聚集的辅助索引(secondary index)。比如,我们还需要按照name这个字段进行查找,并且name这个字段不是唯一的。即,表是按如下的SQL语句定义的:
mysql> create table t ( id int auto_increment, name varchar(30),primary key (id),key(name));
Query OK, 0 rows affected (0.21 sec)
这样的情况下产生了一个非聚集的并且不是唯一的索引。在进行插入操作时,数据页的存放还是按主键id的执行顺序存放,但是对于非聚集索引,叶子节点的插入不再是顺序的了。这时就需要离散地访问非聚集索引页,插入性能在这里变低了。然而这并不是这个name字段上索引的错误,因为B+树的特性决定了非聚集索引插入的离散性。
InnoDB存储引擎开创性地设计了插入缓冲,对于非聚集索引的插入或更新操作,不是每一次直接插入索引页中。而是先判断插入的非聚集索引页是否在缓冲池中。如果在,则直接插入;如果不在,则先放入一个插入缓冲区中,好似欺骗数据库这个非聚集的索引已经插到叶子节点了,然后再以一定的频率执行插入缓冲和非聚集索引页子节点的合并操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对非聚集索引执行插入和修改操作的性能。
插入缓冲的使用需要满足以下两个条件:

索引是辅助索引。
索引不是唯一的。

当满足以上两个条件时,InnoDB存储引擎会使用插入缓冲,这样就能提高性能了。不过考虑一种情况,应用程序执行大量的插入和更新操作,这些操作都涉及了不唯一的非聚集索引,如果在这个过程中数据库发生了宕机,这时候会有大量的插入缓冲并没有合并到实际的非聚集索引中。如果是这样,恢复可能需要很长的时间,极端情况下甚至需要几个小时来执行合并恢复操作。
辅助索引不能是唯一的,因为在把它插入到插入缓冲时,我们并不去查找索引页的情况。如果去查找肯定又会出现离散读的情况,插入缓冲就失去了意义。
可以通过命令SHOW ENGINE INNODB STATUS来查看插入缓冲的信息:
mysql> show engine innodb status\G;
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status: 
=====================================
100727 22:21:48 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 44 seconds
......
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 7545, free list len 3790, seg size 11336,
8075308 inserts, 7540969 merged recs, 2246304 merges
......
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set (0.00 sec)
seg size显示了当前插入缓冲的大小为11 336*16KB,大约为177MB,free list len代表了空闲列表的长度,size代表了已经合并记录页的数量。下面一行可能是我们真正关心的,因为它显示了提高性能了。Inserts代表插入的记录数,merged recs代表合并的页的数量,merges代表合并的次数。merges∶merged recs大约为3∶1,代表插入缓冲将对于非聚集索引页的IO请求大约降低了3倍。
目前插入缓冲存在一个问题是,在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认情况下最大可以占用1/2的缓冲池内存。以下是InnoDB存储引擎源代码中对insert buffer的初始化操作:
/** Buffer pool size per the maximum insert buffer size */
#define IBUF_POOL_SIZE_PER_MAX_SIZE        2
ibuf->max_size = buf_pool_get_curr_size() / UNIV_PAGE_SIZE
                  / IBUF_POOL_SIZE_PER_MAX_SIZE;
这对其他的操作可能会带来一定的影响。Percona已发布一些patch来修正插入缓冲占用太多缓冲池内存的问题,具体的可以到http://www.percona.com/percona-lab.html查找。简单来说,修改IBUF_POOL_SIZE_PER_MAX_SIZE就可以对插入缓冲的大小进行控制,例如,将IBUF_POOL_SIZE_PER_MAX_SIZE改为3,则最大只能使用1/3的缓冲池内存。

猜你喜欢

转载自skywhsq1987.iteye.com/blog/2087242
今日推荐