一、简介
mysql有2个重要的日志模块:redo log(重做日志)和binlog(归档日志)
二、redo log
redo log是WAL技术(Write-Ahead Logging),先写日志,再写磁盘,是InnoDB引擎特有的,主要用于在数据恢复时还原innodb buffer pool中的脏数据页
redo log的日志是一个环
redo log可以保证之前提交的记录都不丢失,这个能力成为crash-safe
由于redo log每次事务提交都要写一次磁盘,而且是顺序的,容易受限于io,所以InnoDB有组提交功能,可以将多个事务redo log的刷盘动作合并,减少磁盘顺序写
innodb_flush_log_at_trx_commit
为0:每秒将日志缓冲区写入log file,并同时flush到磁盘,跟事务提交无关,在机器crash并重启后,会丢失一秒的事务日志数据(并不一定是1s,也许会有延迟,跟操作系统调度有关),性能相对较高,可以在高并发时使用
默认为1:每次事务提交时MySQL都会把log buffer的数据写入log file,并且flush(刷到磁盘)中去,性能相对较低(crash不会丢失数据)
为2:每次事务提交将日志缓冲区写入log file,每秒flush一次到磁盘(crash有可能丢失数据)
建议设置为1,保证异常重启后不丢失数据
innodb_support_xa
默认为1:开启两阶段提交,保证恢复后数据的一致性
为0:关闭两阶段提交,innodb在prepare阶段就什么也不做,这可能会导致binlog的顺序与innodb提交的顺序不一致
三、binlog
binlog是Server层的日志,只依靠binlog没有crash-safe能力
redo log是物理日志,记录的是"在某个数据页上做了什么修改",binlog是逻辑日志,记录的是这个语句的原始逻辑,比如"给ID=2 这一行的c字段加1"
redo log是循环写的,空间固定会用完,binlog是可以追加写的,当文件写到一定大小后会切换到下一个,不会覆盖以前的日志
sync_binlog
默认为0:表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新。这时候的性能是最好的,但是风险也是最大的。因为一旦系统Crash,在binlog_cache中的所有binlog信息都会被丢失
为 > 0:表示每sync_binlog次事务提交,MySQL调用文件系统的刷新操作将缓存刷下去,直到第n次事务再去刷新,有时需要牺牲一定的一致性,可以获得更高的并发和性能
为1:每次事务提交都刷新,性能相对较低,最安全(这里的刷新意思是binlog cache写入disk的过程,会占用磁盘IOPS)
建议设置为1,保证异常重启后binlog不丢失数据
四、两阶段提交
执行器和InnoDB引擎执行update语句的过程:
1、执行器先找引擎取ID=2这一行,引擎用树搜索到这一行,如果数据页在内存中就直接返回,否则从磁盘读入内存返回
2、执行器拿到行数据后,把值+1,再调用引擎接口写入新数据
3、引擎将新数据更新到内存中,同时将更新操作记录到redo log里,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务
4、执行器生成这个操作的binlog,并写入磁盘
5、执行器调用引擎的提交事务接口,引擎把谷歌写入的redo log改成commit状态,更新完成
为什么两阶段提交可以保证数据完整性(包括主从)?
在数据更新到内存后,在写入redo log之前(还未开启事务提交),如果crash则更新当做未完成,不用恢复
在写入redo log完毕进入prepare后,写binlog之前,如果crash则事务回滚
在写入redo log完毕进入prepare后,写binlog时,如果crash则判断,如果binlog完整,则恢复过程中提交事务,如果不完整就回滚事务(binlog有结束标志可以判断是否完整)
在已经处于commit状态时,恢复过程中提交事务
为什么两阶段提交?
因为如果redo log提交完成了,事务就不能回滚了,这时如果binlog写入失败,数据就和binlog日志不一致了,所以要等binlog完成以后再提交redo log事务
为什么redo log和binlog缺一不可?
binlog没有崩溃恢复的能力,它没有记录数据页的更新细节, 如果出现数据页级的丢失无法补回
binlog也有redo log无法踢打的功能:redo log是循环写,起不到归档的作用;binlog复制功能是mysql高可用的基础
参考:MySQL实战45讲