《MySQL技术内幕:InnoDB存储引擎第2版》——2.6 InnoDB关键特性

2.6 InnoDB关键特性

InnoDB存储引擎的关键特性包括:
?插入缓冲(Insert Buffer)
?两次写(Double Write)
?自适应哈希索引(Adaptive Hash Index)
?异步IO(Async IO)
?刷新邻接页(Flush Neighbor Page)
上述这些特性为InnoDB存储引擎带来更好的性能以及更高的可靠性。
2.6.1 插入缓冲
1.?Insert Buffer
Insert Buffer可能是InnoDB存储引擎关键特性中最令人激动与兴奋的一个功能。不过这个名字可能会让人认为插入缓冲是缓冲池中的一个组成部分。其实不然,InnoDB缓冲池中有Insert Buffer信息固然不错,但是Insert Buffer和数据页一样,也是物理页的一个组成部分。
在InnoDB存储引擎中,主键是行唯一的标识符。通常应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引(Primary Key)一般是顺序的,不需要磁盘的随机读取。比如按下列SQL定义表:

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的使用需要同时满足以下两个条件:
?索引是辅助索引(secondary index);
?索引不是唯一(unique)的。
当满足以上两个条件时,InnoDB存储引擎会使用Insert Buffer,这样就能提高插入操作的性能了。不过考虑这样一种情况:应用程序进行大量的插入操作,这些都涉及了不唯一的非聚集索引,也就是使用了Insert Buffer。若此时MySQL数据库发生了宕机,这时势必有大量的Insert Buffer并没有合并到实际的非聚集索引中去。因此这时恢复可能需要很长的时间,在极端情况下甚至需要几个小时。
辅助索引不能是唯一的,因为在插入缓冲时,数据库并不去查找索引页来判断插入的记录的唯一性。如果去查找肯定又会有离散读取的情况发生,从而导致Insert Buffer失去了意义。
用户可以通过命令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显示了当前Insert Buffer的大小为11336×16KB,大约为177MB;free list len代表了空闲列表的长度;size代表了已经合并记录页的数量。而黑体部分的第2行可能是用户真正关心的,因为它显示了插入性能的提高。Inserts代表了插入的记录数;merged recs代表了合并的插入记录数量;merges代表合并的次数,也就是实际读取页的次数。merges:merged recs大约为1∶3,代表了插入缓冲将对于非聚集索引页的离散IO逻辑请求大约降低了2/3。
正如前面所说的,目前Insert Buffer存在一个问题是:在写密集的情况下,插入缓冲会占用过多的缓冲池内存(innodb_buffer_pool),默认最大可以占用到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来修正插入缓冲占用太多缓冲池内存的情况,具体可以到Percona官网进行查找。简单来说,修改IBUF_POOL_SIZE_PER_MAX_SIZE就可以对插入缓冲的大小进行控制。比如将IBUF_POOL_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操作可能分为两个过程:
?将记录标记为已删除;
?真正将记录删除。
因此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'\G;
*************************** 1. row ***************************
Variable_name: innodb_change_buffer_max_size
       Value: 25
1 row in set (0.00 sec)

innodb_change_buffer_max_size值默认为25,表示最多使用1/4的缓冲池内存空间。而需要注意的是,该参数的最大有效值为50。
在MySQL 5.5版本中通过命令SHOW ENGINE INNODB STATUS,可以观察到类似如下的内容:

mysql> SHOW ENGINE INNODB STATUS\G;
*************************** 1. row ***************************
  Type: InnoDB
……
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 34397, seg size 34399, 10875 merges
merged operations:
 insert 20462, delete mark 20158, delete 4215
discarded operations:
 insert 0, delete mark 0, delete 0
……

可以看到这里显示了merged operations和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具体是什么,以及内部怎么实现可能依然模糊,这正是本节所要阐述的内容。
可能令绝大部分用户感到吃惊的是,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(键值),其构造如图2-3所示。《MySQL技术内幕:InnoDB存储引擎第2版》——2.6 InnoDB关键特性

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+树的叶子节点中。《MySQL技术内幕:InnoDB存储引擎第2版》——2.6 InnoDB关键特性

对于插入到Insert Buffer B+树叶子节点的记录(如图2-4所示),并不是直接将待插入的记录插入,而是需要根据如下的规则进行构造:
space、marker、page_no字段和之前非叶节点中的含义相同,一共占用9字节。第4个字段metadata占用4字节,其存储的内容如表2-2所示。
《MySQL技术内幕:InnoDB存储引擎第2版》——2.6 InnoDB关键特性

IBUF_REC_OFFSET_COUNT是保存两个字节的整数,用来排序每个记录进入Insert Buffer的顺序。因为从InnoDB1.0.x开始支持Change Buffer,所以这个值同样记录进入Insert Buffer的顺序。通过这个顺序回放(replay)才能得到记录的正确值。
从Insert Buffer叶子节点的第5列开始,就是实际插入记录的各个字段了。因此较之原插入记录,Insert Buffer B+树的叶子节点记录需要额外13字节的开销。
因为启用Insert Buffer索引后,辅助索引页(space,page_no)中的记录可能被插入到Insert Buffer B+树中,所以为了保证每次Merge Insert Buffer页必须成功,还需要有一个特殊的页用来标记每个辅助索引页(space,page_no)的可用空间。这个页的类型为Insert Buffer Bitmap。
每个Insert Buffer Bitmap页用来追踪16384个辅助索引页,也就是256个区(Extent)。每个Insert Buffer Bitmap页都在16384个页的第二个页中。关于Insert Buffer Bitmap页的作用会在下一小节中详细介绍。
每个辅助索引页在Insert Buffer Bitmap页中占用4位(bit),由表2-3中的三个部分组成。
《MySQL技术内幕:InnoDB存储引擎第2版》——2.6 InnoDB关键特性

4.?Merge Insert Buffer
通过前面的小节读者应该已经知道了Insert/Change Buffer是一棵B+树。若需要实现插入记录的辅助索引页不在缓冲池中,那么需要将辅助索引记录首先插入到这棵B+树中。但是Insert Buffer中的记录何时合并(merge)到真正的辅助索引中呢?这是本小节需要关注的重点。
概括地说,Merge Insert Buffer的操作可能发生在以下几种情况下:
?辅助索引页被读取到缓冲池时;
?Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时;
?Master Thread。
第一种情况为当辅助索引页被读取到缓冲池中时,例如这在执行正常的SELECT查询操作,这时需要检查Insert Buffer Bitmap页,然后确认该辅助索引页是否有记录存放于Insert Buffer B+树中。若有,则将Insert Buffer B+树中该页的记录插入到该辅助索引页中。可以看到对该页多次的记录操作通过一次操作合并到了原有的辅助索引页中,因此性能会有大幅提高。
Insert Buffer Bitmap页用来追踪每个辅助索引页的可用空间,并至少有1/32页的空间。若插入辅助索引记录时检测到插入记录后可用空间会小于1/32页,则会强制进行一个合并操作,即强制读取辅助索引页,将Insert Buffer B+树中该页的记录及待插入的记录插入到辅助索引页中。这就是上述所说的第二种情况。
还有一种情况,之前在分析Master Thread时曾讲到,在Master Thread线程中每秒或每10秒会进行一次Merge Insert Buffer的操作,不同之处在于每次进行merge操作的页的数量不同。
在Master Thread中,执行merge操作的不止是一个页,而是根据srv_innodb_io_capactiy的百分比来决定真正要合并多少个辅助索引页。但InnoDB存储引擎又是根据怎样的算法来得知需要合并的辅助索引页呢?
在Insert Buffer B+树中,辅助索引页根据(space,offset)都已排序好,故可以根据(space,offset)的排序顺序进行页的选择。然而,对于Insert Buffer页的选择,InnoDB存储引擎并非采用这个方式,它随机地选择Insert Buffer B+树的一个页,读取该页中的space及之后所需要数量的页。该算法在复杂情况下应有更好的公平性。同时,若进行merge时,要进行merge的表已经被删除,此时可以直接丢弃已经被Insert/Change Buffer的数据记录。
2.6.2 两次写
如果说Insert Buffer带给InnoDB存储引擎的是性能上的提升,那么doublewrite(两次写)带给InnoDB存储引擎的是数据页的可靠性。
当发生数据库宕机时,可能InnoDB存储引擎正在写入某个页到表中,而这个页只写了一部分,比如16KB的页,只写了前4KB,之后就发生了宕机,这种情况被称为部分写失效(partial page write)。在InnoDB存储引擎未使用doublewrite技术前,曾经出现过因为部分写失效而导致数据丢失的情况。
有经验的DBA也许会想,如果发生写失效,可以通过重做日志进行恢复。这是一个办法。但是必须清楚地认识到,重做日志中记录的是对页的物理操作,如偏移量800,写'aaaa'记录。如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。这就是说,在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是doublewrite。在InnoDB存储引擎中doublewrite的体系架构如图2-5所示。《MySQL技术内幕:InnoDB存储引擎第2版》——2.6 InnoDB关键特性

doublewrite由两部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。可以通过以下命令观察到doublewrite运行的情况:

mysql>SHOW GLOBAL STATUS LIKE 'innodb_dblwr%'\G;
*************************** 1. row ***************************
Variable_name: Innodb_dblwr_pages_written
       Value: 6325194
*************************** 2. row ***************************
Variable_name: Innodb_dblwr_writes
       Value: 100399
2 rows in set (0.00 sec)

可以看到,doublewrite一共写了6?325?194个页,但实际的写入次数为100?399,基本上符合64∶1。如果发现系统在高峰时的Innodb_dblwr_pages_written:Innodb_dblwr_writes远小于64∶1,那么可以说明系统写入压力并不是很高。
如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,InnoDB存储引擎可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。下面显示了一个由doublewrite进行恢复的情况:

090924 11:36:32  mysqld restarted
090924 11:36:33  InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Crash recovery may have failed for some .ibd files!
InnoDB: Restoring possible half-written data pages from the doublewrite
InnoDB: buffer...

若查看MySQL官方手册,会发现在命令SHOW GLOBAL STATUS中Innodb_buffer_pool_pages_flushed变量表示当前从缓冲池中刷新到磁盘页的数量。根据之前的介绍,用户应该了解到,在默认情况下所有页的刷新首先都需要放入到doublewrite中,因此该变量应该和Innodb_dblwr_pages_written一致。然而在MySQL 5.5.24版本之前,Innodb_buffer_pool_pages_flushed总是为Innodb_dblwr_pages_written的2倍,而此Bug直到MySQL5.5.24才被修复。因此用户若需要统计数据库在生产环境中写入的量,最安全的方法还是根据Innodb_dblwr_pages_written来进行统计,这在所有版本的MySQL数据库中都是正确的。
参数skip_innodb_doublewrite可以禁止使用doublewrite功能,这时可能会发生前面提及的写失效问题。不过如果用户有多个从服务器(slave server),需要提供较快的性能(如在slaves erver上做的是RAID0),也许启用这个参数是一个办法。不过对于需要提供数据高可靠性的主服务器(master server),任何时候用户都应确保开启doublewrite功能。
注意 有些文件系统本身就提供了部分写失效的防范机制,如ZFS文件系统。在这种情况下,用户就不要启用doublewrite了。
2.6.3 自适应哈希索引
哈希(hash)是一种非常快的查找方法,在一般情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位数据。而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般为3~4层,故需要3~4次的查询。
InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是通过缓冲池的B+树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。
AHI有一个要求,即对这个页的连续访问模式必须是一样的。例如对于(a,b)这样的联合索引页,其访问模式可以是以下情况:
?WHERE a=xxx
?WHERE a=xxx and b=xxx
访问模式一样指的是查询的条件一样,若交替进行上述两种查询,那么InonDB存储引擎不会对该页构造AHI。此外AHI还有如下的要求:
?以该模式访问了100次
?页通过该模式访问了N次,其中N=页中记录*1/16
根据InnoDB存储引擎官方的文档显示,启用AHI后,读取和写入速度可以提高2倍,辅助索引的连接操作性能可以提高5倍。毫无疑问,AHI是非常好的优化模式,其设计思想是数据库自优化的(self-tuning),即无需DBA对数据库进行人为调整。
通过命令SHOW ENGINE INNODB STATUS可以看到当前AHI的使用状况:

mysql>SHOW ENGINE INNODB STATUS\G;
*************************** 1. row ***************************
Status: 
=====================================
090922 11:52:51 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 15 seconds
……
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 2249, free list len 3346, seg size 5596,
374650 inserts, 51897 merged recs, 14300 merges
Hash table size 4980499, node heap has 1246 buffer(s)
1640.60 hash searches/s, 3709.46 non-hash searches/s
……

现在可以看到AHI的使用信息了,包括AHI的大小、使用情况、每秒使用AHI搜索的情况。值得注意的是,哈希索引只能用来搜索等值的查询,如SELECT*FROM table WHERE index_col='xxx'。而对于其他查找类型,如范围查找,是不能使用哈希索引的,因此这里出现了non-hash searches/s的情况。通过hash searches:non-hash searches可以大概了解使用哈希索引后的效率。
由于AHI是由InnoDB存储引擎控制的,因此这里的信息只供用户参考。不过用户可以通过观察SHOW ENGINE INNODB STATUS的结果及参数innodb_adaptive_hash_index来考虑是禁用或启动此特性,默认AHI为开启状态。
2.6.4 异步IO
为了提高磁盘操作性能,当前的数据库系统都采用异步IO(Asynchronous IO,AIO)的方式来处理磁盘操作。InnoDB存储引擎亦是如此。
与AIO对应的是Sync IO,即每进行一次IO操作,需要等待此次操作结束才能继续接下来的操作。但是如果用户发出的是一条索引扫描的查询,那么这条SQL查询语句可能需要扫描多个索引页,也就是需要进行多次的IO操作。在每扫描一个页并等待其完成后再进行下一次的扫描,这是没有必要的。用户可以在发出一个IO请求后立即再发出另一个IO请求,当全部IO请求发送完毕后,等待所有IO操作的完成,这就是AIO。
AIO的另一个优势是可以进行IO Merge操作,也就是将多个IO合并为1个IO,这样可以提高IOPS的性能。例如用户需要访问页的(space,page_no)为:
(8,6)、(8,7),(8,8)
每个页的大小为16KB,那么同步IO需要进行3次IO操作。而AIO会判断到这三个页是连续的(显然可以通过(space,page_no)得知)。因此AIO底层会发送一个IO请求,从(8,6)开始,读取48KB的页。
若通过Linux操作系统下的iostat命令,可以通过观察rrqm/s和wrqm/s,例如:

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           4.70    0.00    1.60   13.20    0.00   80.50

Device:   rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await  svctm  %util
sdc       3905.67   172.00 6910.33  466.67   168.81    18.15    51.91    19.17    2.59   0.13  97.73

在InnoDB1.1.x之前,AIO的实现通过InnoDB存储引擎中的代码来模拟实现。而从InnoDB 1.1.x开始(InnoDB Plugin不支持),提供了内核级别AIO的支持,称为Native AIO。因此在编译或者运行该版本MySQL时,需要libaio库的支持。若没有则会出现如下的提示:

/usr/local/mysql/bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory

需要注意的是,Native AIO需要操作系统提供支持。Windows系统和Linux系统都提供Native AIO支持,而Mac OSX系统则未提供。因此在这些系统下,依旧只能使用原模拟的方式。在选择MySQL数据库服务器的操作系统时,需要考虑这方面的因素。
参数innodb_use_native_aio用来控制是否启用Native AIO,在Linux操作系统下,默认值为ON:

mysql> SHOW VARIABLES LIKE 'innodb_use_native_aio'\G;
*************************** 1. row ***************************
Variable_name: innodb_use_native_aio
       Value: ON
1 row in set (0.00 sec)

用户可以通过开启和关闭Native AIO功能来比较InnoDB性能的提升。官方的测试显示,启用Native AIO,恢复速度可以提高75%。
在InnoDB存储引擎中,read ahead方式的读取都是通过AIO完成,脏页的刷新,即磁盘的写入操作则全部由AIO完成。
2.6.5 刷新邻接页
InnoDB存储引擎还提供了Flush Neighbor Page(刷新邻接页)的特性。其工作原理为:当刷新一个脏页时,InnoDB存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。这样做的好处显而易见,通过AIO可以将多个IO写入操作合并为一个IO操作,故该工作机制在传统机械磁盘下有着显著的优势。但是需要考虑到下面两个问题:
?是不是可能将不怎么脏的页进行了写入,而该页之后又会很快变成脏页?
?固态硬盘有着较高的IOPS,是否还需要这个特性?
为此,InnoDB存储引擎从1.2.x版本开始提供了参数innodb_flush_neighbors,用来控制是否启用该特性。对于传统机械硬盘建议启用该特性,而对于固态硬盘有着超高IOPS性能的磁盘,则建议将该参数设置为0,即关闭此特性。

;