innodb1.1的新特性

翻译自2010年oreilly mysql user conference中《InnoDB Plugin: Performance Features and Benchmarks》一文 原作者:Jimmy Yang John Russell Calvin Sun InnoDB1.1的新特性

  1. 多个buffer pool实例
  2. 提升恢复性能
  3. 扩展的InnoDB更改buffering
  4. 支持Linux原生的AIO(异步IO)
  5. 多个回滚段
  6. 分拆flush list互斥
  7. 改进purge调度
  8. 改进log_sys互斥
  9. 性能schema支持

多个buffer pool实例

buffer pool互斥是为了保护很多在buffer pool中的数据结构: LRU, Flush List, Free List, Page Hash Table
buffer pool互斥是一个热门互斥(可能不是最热门的)。
在sysbench测试中,它能达到大约700k/s,花费大概50%的时间。
InnoDB性能schema同样可以证明:
5493764597_3c368fed2c_z.jpg

它的解决方法如下:
拆分一个buffer pool成为多个buffer pool实例。这种拆分的结果可以避免所有查询执行代码占用一个以上的buffer pool互斥,只有少数的查询代码会同时占用多个互斥。在16核的服务器上sysbench的读写测试可以提升10%,当然它也会提升只读的性能,在32核的CPU上提升的幅度更大。

使用 innodb_buffer_pool_instances=N,下面是测试结果
5493764599_d0fbcd2abb_z.jpg

改进恢复性能

InooDB的恢复是三段式的。分为: SCAN—REDO–UNDO

这个scan比较慢的,redo日志需要从磁盘上读入到buffer pool中的哈希表中。同时需要跟踪哈希表大小以避免耗尽整个buffer pool。问题是计算哈希表需要通过遍历已分配block的列表。大约有n次方的日志block会被扫描。

redo应用本身也比较慢。脏页需要被插入到flush_list中。列表是根据最早更新到页中的日志序列号(LSN)来排序的。根据在崩溃时候脏页的大数字,列表也可能是大的。而插入列表是根据线性查找的方式插入的。

5494372430_ece2bca41b.jpg

改进scan段的性能:一个简单而有效的方法,在日志头部缓存哈希表的大小。
改进redo段的性能:插入redo日志条目到根据LSN排序的红黑树中。这个方法只对恢复的时候有效,然后红黑树就会被丢弃。实时flush list仍然根据LSN排序的列表.。

5494372432_c4865a0ab9.jpg

这个恢复时间跟InnoDB1.0.6相比要快很多。
下面这个是测试的结果: 60m sysbench OLTP的读写测试。参数是innodb-buffer-pool-size=18g, innodb-log-file-size=2047m,在运行20分钟直接杀死mysqld进程。更改的db页有1007907,redo 2.84GB内容。

5494372426_300a77b267.jpg

我们看到这个差距都是几十倍的提升。
接着我们还进行DBT2的测试。50个数据仓库,数据库大小为9800MB,innodb_log_file_siez=3900MB, buffer_pool=20GB,在测试5分钟后杀死进程进行崩溃测试,结果如下:

5493764611_b4e13e1132.jpg

这个提升速度同样是非常高的。

扩展了的更改缓存

插入buffering: InnoDB可以缓存不存在于buffer pool的插入数据到次要的索引。这个方法避免了由于插入炒作导致的额外I/O。

Delete和purge的buffering:在InnoDB1.1中这个功能是通过扩展insert来进行insert和delete。如果页没在buffer pool中的时候操作就会被缓存。

我们在I/O极限和有次要的索引以及有相当多的DML操作的时候就能看到它的优势。在1.1中我们通过修改innodb_change_buffering来实现,默认的值是all。

下面我们就来进行一下测试。表的数据是5百万行,6个次要索引,表的大小是3G,buffer pool是1G,我们删除10万行。

5493764601_155c313853_m.jpg

我们可以看到这之间的差距是上百倍的。

支持Linux上原生的AIO

除了windows平台,InnoDB已经使用模拟AIO来执行对应的IO操作。(在windows平台,InnoDB使用原生的AIO)。从操作系统的角度来看,模拟AIO仍旧是通过的IO,尽快它看上去从一个查询线程的上下文在一个查询和返回的队列是异步的。在InnoDB1.1版本中已经更改为真实的异步IO。

这是通过Linux的”libaio”库来实现的,它是Linux上内核中原生AIO的接口。它能够改进那些IO压力大的系统,通过”show engine innodb statusG;”我们很可能看到很多由于这个而导致的挂起的读写操作。更多的IO操作指令会在同时被发送到内核,如果OS本身能够服务更多的并发请求,那我们就能很好的利用它。

当然我也可以使用innodb_use_native_aio=0来退回到模拟AIO的状态。

多个回滚段
以前的InnoDB只使用一个回滚段,并且有1023并发的限制(1023指的是在回滚段头部的UNDO日志位置的数字)。而新版的InnoDB是有128个回滚段,而每个回滚段也同时支持1023个事务。

5493764603_73ca4c9c9f.jpg

你不必创建一个新的数据库来利用这个特性。只需要慢慢的关闭就可。这个特性更重要的功能是显著地减少了在回滚段互斥的争执,在前面其它特性中我们也看到一个热门的互斥会扩展成为多个的更改方式。所以这个既是一个性能的提升,同时又是一个扩展。

拆分flush list互斥

flush list是一个在buffer pool中的脏block的列表。在多个事务提交中如果有任何脏block,这些block都会被插入到flush list中。以前当发生这种情况时都会导致占用整个buffer pool互斥。而现在我们是让这个flush list有了它自己单独的名为flush_list_mutex的互斥。这个对于用户来说是透明的,不需要重新设置,同时有了更好的并行性。

更好的purge调度

首先我们了解下purge操作:当我们执行delete的SQL语句在数据库的,一条数据不会被物理的删除。只有当InnoDB丢弃掉这个delete操作的更新update日志的时候才会被物理的删除从数据库中对应的行和索引中。这个移除工作我们称作为purge。它是通过后台的主线程来完成的。

这种方式所导致的问题是在有很多事务的情况下,主线程会因为仅仅进行purge操作而被锁住很长时间。这导致了主线程不能很好的进行flush脏页的工作,也不能进行有规则的checkpoint操作。有可能purge线程会落后于delete操作。

我们的解决方法是分配更多的资源给purge线程所需的。使用单独的线程只进行purge。理论上它不能让purge显得更有效率,但是实际上我们解放了主线程来进行其它更重要的工作比如flush脏页等等。我们可以通过设置innodb_purge_threads=0来恢复到传统模式。innodb_purge_batch_size允许用户指定purge组的大小,它的有效范围是1到5000,默认是20。

5493764607_cd8e261da0_z.jpg