InnoDB二阶段日志提交机制


前些天在查看关于innodb_flush_log_at_trx_commit的官网解释时产生了一些疑问,关于innodb_flush_log_at_trx_commit参数的详细解释参见官网。

其中有一段是这么写的: With a value of 2, the contents of the InnoDB log buffer are written to the log file after each transaction commit and the log file is flushed to disk approximately once per second. 意思是:如果innodb_flush_log_at_trx_commit的值设为2,那么log buffer里的内容会在每次提交时被写入log file,然后logfile也会被flush到disk。 由于innodb的log file据我所知是在硬盘上的ib_logfile,所以对于这里的log file被flush到disk很疑惑,难道log buffer和disk之间还存在了一层可以缓存log file的结构?   在查阅了大量中英文资料后,总算有了初步的了解,暂总结于此。   一、名词解释 在innodb存储引擎中,有一种独有的log file,即redo log file,因此对于innodb存储引擎来说,就存在两种logfile:redo log和binlog. redo log:即data目录下的ib_logfile0,ib_logfile1(个数由innodb_log_files_in_group控制),innodb存储引擎特有,在内存中有相应的redo log buffer。 因此写redo时的3层结构为:redo log buffer--->文件系统缓存中的redo logfile--->disk上的redo log file binlog:默认在data目录下,也可以通过log_bin参数直接指定路径,文件名为默认为<hostname>-bin前缀的文件,在内存中没有log buffer。 因此写binlog时的2层结构为:文件系统缓存中的binlog--->disk上的binlog   二、二阶段日志写的流程 当开启binlog后,如果会话发出了commit的请求,那么在committed之前,一系列的流程为: 1.prepare阶段: 将log buffer的事务更改和事务commit信息写入文件系统缓存中的redo log file,注意log buffer和undo buffer(也叫undo page)是在事务执行过程中就即时生成的(undo默认在系统表空间中,5.6以后也可以自己指定独立的表空间),文件系统缓存中的redo log 是否flush到disk,取决于innodb_flush_log_at_trx_commit参数。 innodb_flush_log_at_trx_commit:
  • 此值为0表示:redo log buffer的内容每秒会被写入文件系统缓存的redo log里,同时被flush(固化)到disk上的redo log file中。
  • 此值为1表示:redo log buffer的内容会在事务commit时被写入文件系统缓存的redo log里,同时被flush(固化)到disk上的redo log file中。
  • 此值为2表示:redo log buffer的内容会在事务commit时被写入文件系统缓存的redo log里,而文件系统缓存的redo log每秒一次被flush(固化)到disk上的redo log file中。
2.写binlog阶段: 此阶段调用两个方法write()和fsync(),前者负责写文件系统缓存中的binlog,后者负责将文件系统缓存中的binlog写入disk上的bin log,前者在此阶段一定会被调用,后者的调用机制由sync_binlog参数控制。 关于sync_binlog参数:
  • sync_binlog=0:表示fsync()的调用完全交给操作系统,即文件系统缓存中的binlog是否刷新到disk完全由操作系统控制。
  • sync_binlog=1:表示在事务提交时,binlog一定会被固化到disk
  • sync_binlog=N(N>1):数据库崩溃时,可能会丢失N-1个事务,具体原理也详见
3.最终commit阶段: 此阶段主要包含:server告诉存储引擎,binlog和redo log都已写好(至少在文件系统缓存级别已经写好),按正常机制提交数据吧,然后向会话返回committed的确认提交信息。   三、故障恢复解析 1.如果在一阶段后崩溃,那么由于binlog未写,数据显然未能提交,算是失败的事务,无需前滚或回滚。(对于Oracle数据库则情况更为复杂,有些大事务即便未提交也可能有已经固化的数据,那么就需要进行回滚。还不清楚mysql的大事务是否也有未提交数据提前写入disk的机制) 2.如果在二阶段后崩溃,那么只有一种情况可以保证数据完全不丢失,即:innodb_flush_log_at_trx_commit和sync_binlog都设置为1,此时redo log和binlog都被固化到磁盘,可以保证commit后未写入的数据在recovery时被前滚提交。如果任意一个不为1,那么都可能造成binlog和redo log不一致的情况,此时很可能丢失事务。 因此,为保证主从完全一致,主库的innodb_flush_log_at_trx_commit和sync_binlog都必须设置为1。   至于5.6以后的为解决并发事务提交异常而出现的3阶段组提交机制,有待继续研究。  

相关内容