事务要保证 ACID
的完整性必须依靠事务日志做跟踪:
事务的日志主要分为三类:redo log
,undo log
和binlog
在写日志的时候,单个日志如果过大,对于读写和同步都会产生影响,所以在日志变大的时候,需要对日志进行一个分组。
redo log
把数据库恢复到崩溃前的状态。redo log包括两部分:一是内存中的日志缓冲(redo log buffer
),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log
file),该部分日志是持久的。
在系统启动的时候,就已经为 redo log 分配了一块连续的存储空间,以顺序追加的方式记录 Redo Log,通过顺序 IO 来改善性能。所有的事务共享 redo loge 的存储空间,它们的 Redo Log 按语句的执行顺序,依次交替的记录在一起。
为了确保每次日志都能写入到事务日志文件中,在每次将 log buffer
中的日志写入日志文件的过程中都会调用一次操作系统的 fsync
操作(即 fsync()
系统调用)。
因为 MySQL
是工作在用户空间的,MySQL
的 log buffer
处于用户空间的内存中。要写入到磁盘上的 log file
中 ,中间还要经过操作系统内核空间的 os buffer
,调用 fsync()
的作用就是将 OS buffer
中的日志刷到磁盘上的 log file
中。
也就是说,从 redo log buffer
写日志到磁盘的 redo log file
中,过程如下:
MySQL
支持用户自定义在commit
时如何将 log buffer
中的日志刷 log file
中。这种控制通过变量innodb_flush_log_at_trx_commit
的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制 commit
动作是否刷新 log buffer
到磁盘。
log buffer
中日志写入到 os buffer
,而是每秒写入 os buffer
并调用 fsync()
写入到log file on disk
中。也就是说设置为 0 时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。
log buffer
中的日志写入 os buffer
并调用 fsync()
刷到log file on disk
中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。
os buffer
,然后是每秒调用 fsync()
将 os buffer
中的日志写入到 log file on disk
。通过上面可以发现,其实值为 2 和 0 的时候,它们的差距并不太大,但 2 却比 0 要安全的多。它们都是每秒从 os buffer
刷到磁盘,它们之间的时间差体现在log buffer
刷到 os buffer
上。因为将 log buffer
中的日志刷新到 os buffer
只是内存数据的转移,并没有太大的开销,所以每次提交和每秒刷入差距并不大。
如果 MySQL 程序 发生了崩溃,logbuffer 会发生丢失,但是 os buffer 还是存在。 如果整个服务器发生了宕机,那么 0 和 2 都会丢失 1s 的数据。
undo log有两个作用:提供回滚和多个行版本控制(MVCC)。
undo log
和redo log
记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
undo log 是逻辑日志,可以理解为:
假设有 2 个数值,分别为 A=1 和 B=2, 然后将 A 修改为 3, B 修改为 4
在 1-8 步骤的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。
如果在 8-9 之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时 redo log 已经持久化。
若在 9 之后系统岩机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据 redo log 把数据刷回磁盘
所以,redo log 其实保障的是事务的持久性和一致性,而 undo log,则保障了事务的原子性。
Binlog 是 server 层的日志,即使是数据的存储的日志(可以理解成业务日志)。redo log 是 InnoDb 的底层的操作日志。
与 redo 日志的区别
binLog的格式
binlog
共有三种存储格式: row, statement 和 混合模式。