插入缓存(Insert Buffer)
插入缓存位于缓冲池中,并且插入缓存是物理页,是和数据页同级的。
一般情况下,主键是行唯一的标识符。通常应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引一般是顺序的,不需要磁盘的随机读取。因为,对于此类情况下的插入,速度还是非常快的。
但如果索引是非聚集的且不唯一,在进行插入操作时,数据的存放对于非聚集索引叶子节点的插入不是顺序的,这时需要离散地访问非聚集索引页,由于随机读取的存在而导致了插入操作性能下降,这是由B+树的特性决定了非聚集索引插入的离散性决定的。
Insert Buffer的设计,对于非聚集索引的插入和更新操作,不是每一次直接插入到索引页中,而是先判断插入非聚集索引页是否在缓冲池中,若存在,则直接插入,不存在,则先放入一个Insert Buffer对象中。这样会让数据库误认为这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge(合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。
两次写(Double Write)
如果说插入缓冲是为了提高写性能的话,那么两次写是为了提高可靠性的。当数据库正在向磁盘写入一个数据页时,如果数据库宕机,并且该数据页还被损坏了,那么这是即使依赖重做日志也没有用了,因为原来的数据页已经被破坏了。因此,我们需要在重做日志之前,复制一份数据页的副本,防止发生上面这种情况,如图所示:
两次写需要额外添加两个部分:
- 内存中的两次写缓冲(doublewrite buffer),大小为2MB
- 磁盘上共享表空间中连续的128页,大小也为2MB
执行流程:
- 当刷新缓冲池脏页时,并不直接写到数据文件中,而是先拷贝至内存中的两次写缓冲区
- 接着从两次写缓冲区分两次写入磁盘共享表空间中,每次写入1MB
- 待第2步完成后,再将两次写缓冲区写入数据文件
这样就可以解决上文提到的部分写失效的问题,因为在磁盘共享表空间中已有数据页副本拷贝,如果数据库在页写入数据文件的过程中宕机,在实例恢复时,可以从共享表空间中找到该页副本,将其拷贝覆盖原有的数据页,再结合重做日志即可恢复数据。其中第2步是额外的性能开销,但由于磁盘共享表空间是连续的,因此开销不是很大。可以通过参数skip_innodb_doublewrite禁用两次写功能,默认是开启的,强烈建议开启该功能。
自适应哈希索引(Adaptive Hash Index)
哈希是一种非常快的查找方法,在一般情况时间复杂度为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。
若交替进行上述两张查询,InnoDB存储引擎不会对该页构造AHI。此外AHI还有如下要求: - 以该模式访问了100次;
- 页通过该模式访问了N次,其中N=页中记录/16。
根据官方文档显示,启用AHI后,读取和写入的速度可以提高2倍,负责索引的链接操作性能可以提高5倍。其设计思想是数据库自由化的,无需DBA对数据库进行人为调整。
异步IO(Async IO)
异步IO可以提高磁盘操作性能,加快IO操作的速度。于异步IO对应的就是同步IO了,即每进行一次IO就需要等待本次IO结束才能进行其它IO操作,如果其中一个IO出现问题,那么其后面的IO操作都无法执行,这显示是一种不好的方式。而在异步IO中,用户可以在发送一个IO请求后立马发送另外一个IO请求,各种执行回调即可,无需等待其它IO执行完回调才能回调。此外,AIO的另外一个优势是进行IO Merge操作,也就是可以将多个IO合并为一个IO操作,这样可以提高IOPS(磁盘性能指标)的性能。
异步IO的实现在InnoDB 1.1.x之前是通过InnoDB存储引擎中的代码来模拟的。但是之后提供了内核级别的异步IO的支持,称为Native AIO。Native AIO需要操作系统提供支持,Windows和Linux都支持,而Mac则未提供,在选择MySQL数据库服务器的操作系统时,需要考虑这方面的因素。
刷新邻接页(Flush Neighbor Page)
InnoDB存储引擎在刷新一个脏页时,会检测该页所在区(extent)的所有页,如果是脏页,那么一起刷新。这样做的好处是通过AIO可以将多个IO写操作合并为一个IO操作。该工作机制在传统机械磁盘下有显著优势。配置文件中的参数innodb_flush_neighbors可以决定是否启用改功能,对于传统机械硬盘建议使用,而对于固态硬盘则可以关闭。
