redo log(重做日志)bin log(归档日志)
redo log

WAL 技术Write-Ahead Loggingcrash-safe
bin log
两种日志有什么区别?
- redo log 是InnoDB,引擎特有的;binlog 是MySQL 的server层实现的,所有的引擎都可以使用
- redo log 是物理日志,记录的是”在某个数据页上做了什么修改”,binlog是逻辑日志,记录的是这个语句的原始逻辑,可以理解为最原始的sql语句
- redo log 是循环写的,空间是会用完的;binlog是可以追加写入的,binlog文件写到一定大小之后会切换到下一个文件继续写入不会覆盖以前的日志内部执行流程
mysql> update T set c=c+1 where ID=2;
- 有了对这两个日志的概念性理解,我们再来看执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程。
- 执行器先从引擎冲拿到 ID=2 这一行,ID是主键,引擎直接用树搜索找到这一行.如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘中读入内存,然后再返回.
- 执行器拿到引擎给的行数据,把这个值加上1,得到一行新的数据,再调用引擎接口写入这行数据.
- 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log当中,此时 redo log 处于 prepare状态,然后告知执行器执行完成,随时可以提交事务.
- 执行器生成这个操作的 binlog,并把binlog写入磁盘
- 执行器调用引擎提交事务接口,引擎把刚刚写入的redo log 改成 commit状态,更新完成
两阶段提交
为什么?
仍然用前面的 update 语句来做例子。假设当前 ID=2 的行,字段 c 的值是 0,再假设执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash,会出现什么情况呢?
mysql> update T set c=c+1 where ID=2;
- 先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
- 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。你可能会说,这个概率是不是很低,平时也没有什么动不动就需要恢复临时库的场景呀?其实不是的,不只是误操作后需要用这个过程来恢复数据。当你需要扩容的时候,也就是需要再多搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用 binlog 来实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况。简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
