首先明确一下InnoDB修改数据的基本流程。当我们想要修改DB上某一行数据的时候,InnoDB是把数据从磁盘读取到内存的缓冲池上进行修改。这个时候数据在内存中被修改,与磁盘中相比就存在了差异,我们称这种有差异的数据为脏页。InnoDB对脏页的处理不是每次生成脏页就将脏页刷新回磁盘,因为这样会产生海量的I/O操作,严重影响InnoDB的处理性能。既然脏页与磁盘中的数据存在差异,那么如果在此期间DB出现故障就会造成数据丢失。为了解决这个问题,redo log就应运而生了。
我们着重看看redo log是怎么一步步写入磁盘的。redo log本身由两部分所构成,即重做日志缓冲(redo log buffer)和重做日志文件(redo log file)。这样的设计同样也是为了调和内存与磁盘的速度差异。InnoDB写入磁盘的策略可以通过innodb_flush_log_at_trx_commit这个参数来控制。
DB宕机后重启,InnoDB会首先去查看数据页中LSN的数值,即数据页被刷新回磁盘的LSN(LSN实际上就是InnoDB使用的一个版本标记的计数)的大小,然后去查看redo log的LSN大小。如果数据页中的LSN值大,就说明数据页领先于redo log刷新回磁盘,不需要进行恢复;反之,需要从redo log中恢复数据。
当一个日志文件写满后,InnoDB会自动切换到另一个日志文件,但切换时会触发数据库检查点checkpoint(checkpoint所做的事就是把脏页刷新回磁盘,当DB重启恢复时只需要恢复checkpoint之后的数据即可),导致InnoDB缓存脏页的小批量刷新,明显降低InnoDB的性能。可以通过增大log file size避免一个日志文件过快被写满,但是如果日志文件设置得过大,恢复时将需要更长的时间,同时也不便于管理。一般来说,平均每半个小时写满一个日志文件比较合适。
参数innodb_log_buffer_size决定InnoDB重做日志缓冲池的大小。对于可能产生大量更新记录的大事务,增加innodb_log_buffer_size的大小,可以避免InnoDB在事务提交前就执行不必要的日志写入磁盘操作。因此,对于会在一个事务中更新、插入或删除大量记录的应用,可以通过增大innodb_log_buffer_size来减少日志写磁盘操作,提高事务处理性能。