InnoDB存储引擎支持组提交。即当一个事务在进行提交时允许其他事务的重做日志写入当重做日志缓冲,当下次写入到重做日志文件时,仅需一次fsync就能刷新多个事务提交的重做日志。这部分的实现在文件log0log.c的函数log_write_up_to中完成。(这里不考虑MySQL 5.5启用二进制日志带来的组提交失效问题)
一个事务可能有多个重做日志条目,重做日志条目的写入顺序并不要求按事务的提交顺序进行写入(这和二进制日志不同,一个事务会产生多个多做日志条目)。由于重做日志的条目最终会写入到redo log buffer。因此需要一个轻量级的锁来控制不同事务向redo log buffer的写入,在InnoDB存储引擎中,这由log_sys->mutex进行控制。
重做条目保存在数据结构为mtr_t的对象中。因此重做日志写入的整个过程如下图所示:
在从mtr向redo log buffer的写入由函数log_write_low完成,并有log_sys->mutex进行并发保护。redo log buffer写到redo log file由函数log_write_up_to完成,同样需要通过log_sys->mutex进行并发保护。所以可以发现log_sys->mutex成为了一个热点,另一个重点是:重做日志条目写入到redo log buffer变为了串行。
Oracle数据库通过redo copy latch来解决此问题。其将写入redo log buffer分为了两个过程:
分配redo log buffer空间(alloc latch)
通过memcpy完成实际重做日志的写入(redo copy latch,可以有多个,默认为CPU*2)
这两个过程通过两个不同的latch来完成并发控制,这样的设计非常有道理的。因为在InnoDB存储引擎中,上述两个过程都需要通过log_sys->mutex进行保护,而实际只需在redo log buffer中分配完空间就可以释放。这样就能提高向redo log buffer写入的并发性能。这里我采用了类似Oracle数据库的思想来对log_sys->mutex进行优化。
想法很简单,然而实现还是需要对很多重做日志函数进行重构,代码显得也不怎么优美和简洁。然而,初步测试结果是令人欣慰和鼓舞的,在我rMBP的笔记本上测试结果如下所示:
测试采用sysbench的update_non_index.lua模式,进行最为简单的乐观更新测试。可以看到优化后的性能可以有30%的提升。此外,还简单做了一些故障恢复的测试,目前宕机都能通过redo log file进行有效地恢复。
由于从设想到完成第一版本的开发时间非常短暂,之后需要对代码进行梳理,并进行更为全面的测试。之后会开放这个补丁下载,最终整合到InnoSQL中