Junhc

岂止于博客

MySQL事务的实现

redo

在InnoDB存储引擎中,事务日志通过重做日志文件(redo log)和InnoDB存储引擎日志缓冲(InnoDB log buffer)来实现。
当开始一个事务时,会记录该事务的一个LSN(Log Sequence Number, 日志序列号)。
当事务执行时,会往InnoDB存储引擎的日志缓冲里插入事务日志。
当事务提交时,必须将InnoDB存储引擎的日志缓冲写入磁盘。
也就是在写数据前,需要先写日志。这种方式称为预写日志方式。
Log sequence number 表示当前的LSN
Log flushed up to 表示刷新到重做日志文件的LSN
Last checkpoint at 表示刷新到磁盘的LSN

在此处需要注意一点,一般所说的log file并不是磁盘上的物理日志文件,而是操作系统缓存中的log file,
官方手册上的意思也是如此(例如: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)。
但说实话,这不太好理解,既然都称为file了,应该已经属于物理文件了。
所以在本文后续内容中都以os buffer或者file system buffer来表示官方手册中所说的Log file,然后log file则表示磁盘上的物理日志文件,即log file on disk。

另外,之所以要经过一层os buffer,是因为open日志文件的时候,open没有使用O_DIRECT标志位,该标志位意味着绕过操作系统层的os buffer,IO直写到底层存储设备。
不使用该标志位意味着将日志进行缓冲,缓冲到了一定容量,或者显式fsync()才会将缓冲中的刷到存储设备。使用该标志位意味着每次都要发起系统调用。
-- redo log参数
show global variables like '%innodb%log%'
redo log参数设置
  • innodb_api_enable_binlog OFF
  • innodb_flush_log_at_timeout 1
  • innodb_flush_log_at_trx_commit 事务从redo log buffer刷新到redo log file策略,默认值为1
    • 0表示事务提交时,每秒写入os buffer并调用fsync()写入到log file on disk。数据库宕机,最多丢失1秒钟的数据。
    • 1表示事务提交时,将redo log buffer写入os buffer并调用fsync()刷入redo log file,这种方式即使系统崩溃也不会丢失数据,但是因为每次提交都写入磁盘,IO的性能较差。
    • 2表示事务提交时,写入os buffer,然后每秒调用fsync()刷入redo log file。
  • innodb_locks_unsafe_for_binlog OFF
  • innodb_log_buffer_size 重做日志缓冲大小,默认值为16M
  • innodb_log_compressed_pages ON
  • innodb_log_file_size redo log大小,单位字节,默认值为48M
  • innodb_log_files_in_group redo log group大小,日志组中存在多少个redo log文件,默认值为2
  • innodb_log_group_home_dir ./
  • innodb_mirrored_log_groups 1
  • innodb_online_alter_log_max_size 134217728
  • innodb_undo_logs 128
undo

重做日志记录了事务的行为,可以很好地通过其进行“重做”。
但是事务有时还需要撤销,这时就需要undo。
undo与redo正好相反,对于数据库进行修改时,数据库不但会产生redo,而且还会产生一定量的undo。

与redo不同的是,redo存放在重做日志文件中,undo存放在数据库内部的一个特殊段中,
这称为undo段(undo segment),位于共享表空间内。

参考资料