Redo Log是InnoDB存储引擎特有的日志,位于引擎层;Redo Log 是一种物理日志,记录的是“在某个数据页上做了什么修改”;
在事务ACID与隔离级别一文中我们了解到,事务的Durability(持久性)是通过Redo Log来实现的。
Redo Log采用了预写式日志(Write-Ahead Logging,WAL)技术。在计算机科学中,预写式日志(Write-Ahead Logging,WAL)技术是关系数据库系统中用于提供Atomicity(原子性)和Durability(持久性)的一系列技术。在使用 WAL 的系统中,所有的修改在提交之前都要先写入 log 文件中。
在事务操作数据时,将新数据备份到Redo Log,这与Undo Log刚好相反。在事务提交时,只需要在内存中将数据修改为新数据,并且将Redo Log持久化到磁盘(顺序I/O)即可,而无需将新数据直接持久化到磁盘。
InnoDB引擎会在适当的时候(间隔一定的时间,或者Redo Log Buffer满),将新数据按照Redo Log持久化到磁盘;
InnoDB 的 Redo Log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 100MB,那么总共就可以记录 400MB 的操作记录。从头开始写,写到末尾就又回到开头循环写,如图所示。
write position 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write position 和 checkpoint 之间的是还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示写满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
有了 Redo Log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
crash-safe:
只要更新的数据记在了Redo Log上或者磁盘上,就可以保证数据的持久性。
本质上说,crash-safe 就是落盘处理,将数据存储到了磁盘上,断电重启也不会丢失。
binlog 是 MySQL Server 层的日志,而不是存储引擎自带的日志,是所有存储引擎共用的。它记录了所有的 DDL 和 DML(不包含数据查询语句)语句,而且是以事件形式记录,还包含语句所执行的消耗的时间等。
binlog 是一种逻辑日志,他里边所记录的是一条 SQL 语句的原始逻辑,例如给某一个字段 +1,注意这个区别于 Redo Log 的物理日志(在某个数据页上做了什么修改)。
之所以将binlog称为归档日志,是因为binlog不会像 Redo Log 一样擦掉之前的记录循环写,而是一直记录(超过有效期才会被清理),如果超过单日志的最大值(默认1G,可以通过变量 max_binlog_size 设置),则会新起一个文件继续记录。
正是由于binlog有归档的作用,所以binlog主要用作主从同步和数据库基于时间点的还原。
如果是主从模式下,binlog是必须的,因为从库的数据同步依赖的就是binlog;
如果是单机模式,并且不考虑数据库基于时间点的还原,binlog就不是必须,因为有Redo Log就可以保证crash-safe能力了;但如果万一需要回滚到某个时间点的状态,这时候就无能为力,所以建议binlog还是一直开启;
在实际操作中,Redo Log是分两步写的,中间穿插了binlog写;
因为在主从模式下,Redo Log会影响主库的数据,而binlog会影响从库的数据,所以必须保证Redo Log与binlog的一致,否则就会造成主从数据不一致;这里的Redo Log和binlog其实就是很典型的分布式事务场景,因为两者本身就是两个独立的个体,要想保持一致,就必须使用分布式事务的解决方案来处理。而将Redo Log分成了两步,其实就是使用了两阶段提交协议(Two-phase Commit,2PC)。
innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这样可以保证 MySQL 异常重启之后数据不丢失。
sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这样可以保证 MySQL 异常重启之后 binlog 不丢失。
我们可以使用反证法。如果不是用两阶段提交,那么必然先写Redo Log或者先写binlog。
如果使用了两阶段提交: