自系统开始运行,就不断的在修改页面,也就意味着会不断的生成 redo 日志。redo 日志的量在不断的递增,就像人的年龄一样,自打出生起就不断递增, 永远不可能缩减了。
InnoDB 为记录已经写入的 redo 日志量,设计了一个称之为 Log Sequence Number 的全局变量,翻译过来就是:日志序列号,简称 LSN。规定初始的 lsn 值为 8704(也就是一条 redo 日志也没写入时,LSN 的值为 8704)。
我们知道在向 log buffer 中写入 redo 日志时不是一条一条写入的,而是以一个 Mini-Transaction 生成的一组 redo 日志为单位进行写入的。从上边的描述中可以看出来,每一组由 Mini-Transaction 生成的 redo 日志都有一个唯一的 LSN 值与其对应,LSN 值越小,说明 redo 日志产生的越早。
flushed_to_disk_lsn
redo 日志是首先写到 log buffer 中,之后才会被刷新到磁盘上的 redo 日志文件。InnoDB 中有一个称之为 buf_next_to_write 的全局变量,标记当前 log buffer 中已经有哪些日志被刷新到磁盘中了。
我们前边说 lsn 是表示当前系统中写入的 redo 日志量,这包括了写到 log buffer 而没有刷新到磁盘的日志,相应的,InnoDB 也有一个表示刷新到磁盘中的redo日志量的全局变量,称之为 flushed_to_disk_lsn。系统第一次启动时,该变量的值和初始的 lsn 值是相同的,都是 8704。随着系统的运行,redo 日志被不断写入 log buffer,但是并不会立即刷新到磁盘,lsn 的值就和 flushed_to_disk_lsn 的值拉开了差距。我们演示一下:
系统第一次启动后,向 log buffer 中写入了 mtr_1、mtr_2、mtr_3 这三个 mtr
产生的 redo 日志,假设这三个 mtr 开始和结束时对应的 lsn 值分别是:
- mtr_1:8716 ~ 8916
- mtr_2:8916 ~ 9948
- mtr_3:9948 ~ 10000
此时的 lsn 已经增长到了 10000,但是由于没有刷新操作,所以此时flushed_to_disk_lsn 的值仍为 8704。
随后进行将log buffer 中的block 刷新到redo 日志文件的操作,假设将 mtr_1 和mtr_2 的日志刷新到磁盘,那么 flushed_to_disk_lsn 就应该增长mtr_1 和mtr_2 写入的日志量,所以 flushed_to_disk_lsn 的值增长到了 9948。
综上所述,当有新的 **redo 日志写入到 log buffer 时,首先 lsn 的值会增长, 但flushed_to_disk_lsn 不变,随后随着不断有log buffer 中的日志被刷新到磁盘上, flushed_to_disk_lsn 的值也跟着增长。如果两者的值相同时,说明 log buffer 中的所有 redo 日志都已经刷新到磁盘中了。
应用程序向磁盘写入文件时其实是先写到操作系统的缓冲区中去,如果某个写入操作要等到操作系统确认已经写到磁盘时才返回,那需要调用一下操作系统提供的 fsync 函 数 。 其实只有当系统执行了 fsync 函数后 , flushed_to_disk_lsn 的值才会跟着增长,当仅仅把log buffer 中的日志写入到操作系统缓冲区却没有显式的刷新到磁盘时,另外的一个称之为write_lsn 的值跟着增长。