在innodb存储引擎中,事务日志通过重做(redo)日志文件和InnoDB存储引擎的日志缓冲(InnoDB Log Buffer)来实现。当开始一个事务时,会记录该事务的一个LSN(Log Sequence Number,日志序列号);当事务执行时,会往InnoDB存储引擎的日志缓冲里插入事务日志;当事务提交时必须将InnoDB存储引擎的日志缓冲写到磁盘(默认的实现,即innodb_flush_log_at_trx_commit=1)。也就是在写数据前,需要先写日志,这种方式称为预写日志方式(Write-Ahead Logging,WAL)。
InnoDB存储引擎通过预写日志的方式来保证事务的完整性。这意味着磁盘上存储的数据页和内存缓冲池的页是不同步的,对于内存缓冲池中页的修改,先是写入重做日志文件,然后再写入磁盘,因此是一种异步的方式。可以通过命令show engine innodb status来观察当前磁盘和日志的“差距”;
首先建立一张表z,然后建立一个往表z中导入数据的存储过程load test。通过命令SHOW ENGINE INNODB STATUS观察当前的重做日志情况:
Log sequence number表示当前的ISN, Log flushed up to表示刷新到重做日志文件的LSN, Last checkpoint at表示刷新到磁盘的LSN。因为当前没有任何操作,所以这三者的值是一样的。接着开始导入10000条记录:
mysql>call load test(10000);
mysql> show engine innodb status\G;
······
---
LOG
---
Log sequence number 11 3047672789
Log flushed up to 11 3047672789
Last checkpoint at 11 3047174608
0 pending log writes, 0 pending chkp writes
143 log i/o' s done, 0.08 log i/o' s second
······
1 row in set (0.00 sec)
这次SHOW ENGINE INNODB STATUS的结果就不同了, Log sequence number的LSN为113047672789, Log flushed up to的LSN为113047672789, Last checkpoint at的LSN为113047174608,可以把 Log flushed up to和 Last checkpoint at的差值498181(~486.5K)理解为重做日志产生的增量(以字节为单位)。
虽然在上面的例子中, Log sequence number和 Log flushed up to的值是相等的,但是在实际的生产环境中,该值有可能是不同的。因为在一个事务中从日志缓冲刷新到重做日志文件,并不只是在事务提交时发生,每秒都会有从日志缓冲刷新到重做日志文件的动作(这部分内容我们在362小节已经讲解过了)。下面是一个生产环境下重做日志的信息:
mysql> show engine innodb status\G;
······
---
LOG
---
Log sequence number 203318213447
Log flushed up to 203318213326
Last checkpoint at 203252831194
1 pending log writes, 0 pending chkp writes
103447 log i/o' s done, 7.00 log i/o' s second
······
1 row in set (0.00 sec)
可以看到,在生产环境下Log sequence number、Log flushed up to、Last checkpoint at个值可能是不同的。