InnoDB存储引擎的关键特性包括插入缓冲、两次写(double write)、自适应哈希索引(adaptive hash index)。这些特性为InnoDB存储引擎带来了更好的性能和更高的可靠性。html
插入缓冲是InnoDB存储引擎关键特性中最使人激动的。不过,这个名字可能会让人认为插入缓冲是缓冲池中的一个部分。其实否则,InnoDB缓冲池中有Insert Buffer信息当然不错,可是Insert Buffer和数据页同样,也是物理页的一个组成部分。mysql
主键是行惟一的标识符,在应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的。所以,插入汇集索引通常是顺序的,不须要磁盘的随机读取。sql
好比说咱们按下列SQL定义的表:create table t(id int auto_increment,name varchar(30),primary key(id));数据库
id列是自增加的,这意味着当执行插入操做时,id列会自动增加,页中的行记录按id执行顺序存放。通常状况下,不须要随机读取另外一页执行记录的存放。所以,在这样的状况下,插入操做通常很快就能完成。可是,不可能每张表上只有一个汇集索引,在更多的状况下,一张表上有多个非汇集的辅助索引(secondary index)。好比,咱们还须要按照name这个字段进行查找,而且name这个字段不是惟一的。服务器
表是按以下的SQL语句定义的:create table t (id int auto_increment,name varchar(30),primary key(id),key(name));架构
这样的状况下产生了一个非汇集的而且不是惟一的索引。在进行插入操做时,数据页的存放仍是按主键id的执行顺序存放,可是对于非汇集索引,叶子节点的插入再也不是顺序的了。这时就须要离散地访问非汇集索引页,插入性能在这里变低了。然而这并非这个name字段上索引的错误,由于B+树的特性决定了非汇集索引插入的离散性。app
InnoDB存储引擎开创性地设计了插入缓冲,对于非汇集索引的插入或更新操做,不是每一次直接插入索引页中,而是先判断插入的非汇集索引页是否在缓冲池中。若是在,则直接插入;若是不在,则先放入一个插入缓冲区中,好似欺骗数据库这个非汇集的索引已经插到叶子节点了,而后再以必定的频率执行插入缓冲和非汇集索引页子节点的合并操做,这时一般能将多个插入合并到一个操做中(由于在一个索引页中),这就大大提升了对非汇集索引执行插入和修改操做的性能。函数
插入缓冲的使用须要知足如下两个条件:性能
当知足以上两个条件时,InnoDB存储引擎会使用插入缓冲,这样就能提升性能了。不过考虑一种状况,应用程序执行大量的插入和更新操做,这些操做都涉及了不惟一的非汇集索引,若是在这个过程当中数据库发生了宕机,这时候会有大量的插入缓冲并无合并到实际的非汇集索引中。若是是这样,恢复可能须要很长的时间,极端状况下甚至须要几个小时来执行合并恢复操做。优化
辅助索引不能是惟一的,由于在把它插入到插入缓冲时,咱们并不去查找索引页的状况。若是去查找确定又会出现离散读的状况,插入缓冲就失去了意义。
查看插入缓冲的信息:
show engine innodb status\G
seg size显示了当前插入缓冲的大小为2*16KB,free list len表明了空闲列表的长度,size表明了已经合并记录页的数量。
下面一行多是咱们真正关心的,由于它显示了提升性能了。inserts表明插入的记录数,merged recs表明合并的页的数量,merges表明合并的次数。
merged recs:merges大约为3:1,表明插入缓冲将对于非汇集索引页的IO请求大约下降了3倍。
问题:
目前插入缓冲存在一个问题是,在写密集的状况下,插入缓冲会占用过多的缓冲池内存,默认状况下最大能够占用1/2的缓冲池内存。Percona已发布一些patch来修正插入缓冲占用太多缓冲池内存的问题,具体的能够到http://www.percona.com/percona-lab.html查找。简单来讲,修改IBUF_POOL_SIZE_PER_MAX_SIZE就能够对插入缓冲的大小进行控制,例如,将IBUF_POOL_SIZE_PER_MAX_SIZE改成3,则最大只能使用1/3的缓冲池内存。
若是说插入缓冲带给InnoDB存储引擎的是性能,那么两次写带给InnoDB存储引擎的是数据的可靠性。当数据库宕机时,可能发生数据库正在写一个页面,而这个页只写了一部分(好比16K的页,只写前4K的页)的状况,咱们称之为部分写失效(partial page write)。在InnoDB存储引擎未使用double write技术前,曾出现过由于部分写失效而致使数据丢失的状况。
有人也许会想,若是发生写失效,能够经过重作日志进行恢复。这是一个办法。可是必须清楚的是,重作日志中记录的是对页的物理操做,如偏移量800,写'aaaa'记录。若是这个页自己已经损坏,再对其进行重作是没有意义的。这就是说,在应用(apply)重作日志前,咱们须要一个页的副本,当写入失效发生时,先经过页的副原本还原该页,再进行重作,这就是doublewrite。
InnoDB存储引擎doublewrite的体系架构如图2-4所示:
doublewrite由两部分组成:一部分是内存中的doublewrite buffer,大小为2MB;另外一部分是物理磁盘上共享表空间中连续的128个页,即两个区(extent),大小一样为2MB(页的副本)。当缓冲池的脏页刷新时,并不直接写磁盘,而是会经过memcpy函数将脏页先拷贝到内存中的doublewrite buffer,以后经过doublewrite buffer再分两次,每次写入1MB到共享表空间的物理磁盘上,而后立刻调用fsync函数,同步磁盘,避免缓冲写带来的问题。在这个过程当中,由于doublewrite页是连续的,所以这个过程是顺序写的,开销并非很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。
能够经过如下命令观察到doublewrite运行的状况: show global status like 'innodb_dblwr%'\G
doublewrite一共写了18 445个页,但实际的写入次数为434,(42:1) 基本上符合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:Error:space id in fsp header 0,but in the page header 4294967295 InnoDB:Error:tablespace id 4294967295 in file./test/t.ibd is not sensible InnoDB:Error:tablespace id 0 in file./test/t2.ibd is not sensible 090924 11:36:33 InnoDB:Operating system error number 40 in a file operation. InnoDB:Error number 40 means'Too many levels of symbolic links'. InnoDB:Some operating system error numbers are described at InnoDB:http://dev.mysql.com/doc/refman/5.0/en/operating-system-error-codes.html InnoDB:File name./now/member InnoDB:File operation call:'stat'. InnoDB:Error:os_file_readdir_next_file()returned-1 in InnoDB:directory./now InnoDB:Crash recovery may have failed for some.ibd files! InnoDB:Restoring possible half-written data pages from the doublewrite InnoDB:buffer……
参数skip_innodb_doublewrite能够禁止使用两次写功能,这时可能会发生前面说起的写失效问题。不过,若是你有多台从服务器(slave server),须要提供较快的性能(如slave上作的是RAID0),也许启用这个参数是一个办法。不过,在须要提供数据高可靠性的主服务器(master server)上,任什么时候候咱们都应确保开启两次写功能。
注意:有些文件系统自己就提供了部分写失效的防范机制,如ZFS文件系统。在这种状况下,咱们就不要启用doublewrite了。
哈希(hash)是一种很是快的查找方法,通常状况下查找的时间复杂度为O(1)。经常使用于链接(join)操做,如SQL Server和Oracle中的哈希链接(hash join)。可是SQL Server和Oracle等常见的数据库并不支持哈希索引(hash index)。MySQL的Heap存储引擎默认的索引类型为哈希,而InnoDB存储引擎提出了另外一种实现方法,自适应哈希索引(adaptive hash index)。
InnoDB存储引擎会监控对表上索引的查找,若是观察到创建哈希索引能够带来速度的提高,则创建哈希索引,因此称之为自适应(adaptive)的。自适应哈希索引经过缓冲池的B+树构造而来,所以创建的速度很快。并且不须要将整个表都建哈希索引,InnoDB存储引擎会自动根据访问的频率和模式来为某些页创建哈希索引。
根据InnoDB的官方文档显示,启用自适应哈希索引后,读取和写入速度能够提升2倍;对于辅助索引的链接操做,性能能够提升5倍。自适应哈希索引是很是好的优化模式,其设计思想是数据库自优化(self-tuning),即无需DBA对数据库进行调整。
查看当前自适应哈希索引的使用情况:show engine innodb status\G
如今能够看到自适应哈希索引的使用信息了,包括自适应哈希索引的大小、使用状况、每秒使用自适应哈希索引搜索的状况。值得注意的是,哈希索引只能用来搜索等值的查询,如select * from table where index_col='xxx',而对于其余查找类型,如范围查找,是不能使用的。所以,这里出现了non-hash searches/s的状况。用hash searches:non-hash searches命令能够大概了解使用哈希索引后的效率。
因为自适应哈希索引是由InnoDB存储引擎控制的,因此这里的信息只供咱们参考。不过咱们能够经过参数innodb_adaptive_hash_index来禁用或启动此特性,默认为开启。