之前写过一篇博文,《不好的MySQL过程编写习惯》(http://www.cnblogs.com/wingsless/p/5041838.html)。这篇博文里强调了不要循环的提交事务,尽量将可以放在一起的SQL同一个事务提交,会快很多很多。博文中提到了redo的问题,因此,结合最近编写新员工培训材料的感悟,简单的介绍一些InnoDB和Redo的事情。
InnoDB的内存中有redo log buffer,磁盘上还有redo log file,redo用于在宕机之后恢复数据,保证数据的持久性。
一般来讲,最符合ACID的redo工作方式应该是这样的:事务提交时,内存中的redo buffer内容写入文件中,并刷回磁盘(flush,官方文档中解释该动作是将磁盘缓存的数据flush回文件中)。此时buffer pool中的数据块被修改成为脏页,但是并不写回磁盘中,而是在master thread的循环中慢慢写回去,这样实际上日志的写入和数据文件的写入不是同步进行的,两者之间会有一定的时间差,这种方式就叫做预写日志(WAL)。一旦系统宕机,内存中的数据立刻丢失,下次启动数据库时,没有来得及写回数据文件的数据可以从redo log中恢复。
因此在上篇博文中的过程,会在每一个事务提交时写一次日志,这样会带来很多的磁盘IO,因此效率非常低下,而单个事务提交只会造成一次IO,所以效率提升非常显著。但是,InnoDB还支持另外的一种模式,这个模式由innodb_flush_log_at_trx_commit参数控制,默认情况下是1,代表上面说的那种方式,每次事务提交都会写redo日志。
在一次试验中,我仍然保持存储过程循环提交事务,但是我将参数调整到了2,这样的效率提升也很明显,稍微慢于单事务提交。参数为2的意义是,每次提交事务的时候,也会写日志文件,但是并不调用fsync函数(刷盘的函数)将日志刷回磁盘,而是一秒一次的调度fsync函数。因此也会带来不少的效率提升。这样做的问题很明显,如果遇到宕机,会丢失一秒左右的数据。
当然这个参数还能调整成0,代表事务提交时不作任何操作,每隔一秒才会将redo buffer的数据写入日志并刷回磁盘中。这种方式看起来就明显快的多了,但是却是最不符合ACID原则的做法。
当然了,刷磁盘这个事情还会牵扯一些别的参数,那就不在本文的讨论范围之内了,未来如果有学习心得我会写下来的。
实际上,如果不在乎一秒的数据丢失(一秒的数据有时候真的很多很多),可以将该参数选择为2,但是最好还是选择为1。程序在写数据库的时候,可以采用批量提交的方式,速度非常快。这是程序设计的问题了,也不在讨论范围内了。