此文为极客时间:MySQL实战45讲的 2、15节日志相关部分和网上一些相关文章的内容的总结
一、redo log
redo log 又叫重做日志,提供的是数据丢失后的前滚操作。
redo log 是 innodb 引擎独有的日志,使用了 WAL 技术(Write-Ahead Logging),也就是预写日志。它的关键点就是先写日志,再写磁盘。对应到 mysql 中具体操作,就是每次更新操作,先写日志,然后更新内存数据,最后等系统压力小的时候再进行IO更新磁盘数据。避免了每一次更新都需要进行IO操作。redo log 是保证了事务持久性的关键。
redo log 一般用在数据库恢复的情况:
- 如果是正常运行的实例的话,数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程,甚至与 redo log 毫无关系。
- 在崩溃恢复场景中,InnoDB 如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让 redo log 更新内存内容。更新完成后,内存页变成脏页,就回到了第一种情况的状态。
另外,redo log 与 undo log 都被叫做事务日志。
redo log 是一个物理日志,我们知道数据库引擎加载是按“页”来的,redo log记录的就是每个“页”上的数据发生的变化。但是不像 binlog 那样,redo log 不记录 sql,而是以类似 session_id + date + file_id + block_id + 修改数据这样的格式去记录数据。
redo log 的日志文件大小是根据配置固定的,如果有一组有四个文件,每个文件的大小是 1GB,那么总共就只能记录 4GB 的日志。
因为 redo log 是前滚日志,也就是说一旦事务成功提交且数据持久化落盘之后,此时日志中的对应事务数据记录就失去了意义。所以 redo log 类似一个环形链表,从前往后写,到底了就删除最前面的再回到开头往后写。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的并且在日志中有记录都数据可以找回,这个能力称为 crash-safe。
二、undo log
undo log 又叫回滚日志。事务未提交之前,undo log 保存了未提交之前的版本数据,可作为数据旧版本快照供其他并发事务进行快照读。
因此,他能够提供两个功能:
回滚:当执行 rollback 时,就可以从 undo log 中的逻辑记录读取到相应的内容并进行回滚。
简单的说:如果我们执行了insert操作,那么日志中就会新增一条相反的 delete 的 sql;
多行版本控制(MVCC):当读取的某一行被其他事务锁定时,它可以从 undo log 中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。
undo log 保证了事务的原子性。
三、binlog
binlog 又叫二进制日志。是 Server 层特有的日志,无论哪个引擎都能使用。
它被用于记录 mysql 的数据更新(即使更新零条或者删除零条也会记录)。
binlog有三种工作模式:
- Row :日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。
- Statement:每一条被修改数据的sql都会记录到 master 的 binlog 中,slave 在复制的时候sql进程会解析成和原来 master 端执行过的相同的sql再次执行。
- Mixed:结合了 Row 和 Statement 的优点,同时 binlog 结构也更复杂。
binlog 和 redo log 的区别如下:
- binlog 是 mysql 自带的,redo log 是 innodb 引擎自带的
- binlog 记录的是每一行数据的变化或修改数据的 sql,redo log 记录的是数据页的变化
- binlog 能够实现归档功能,通过 binlog 可以实现备份,redo log 是循环写的,历史日志不会一直保留
- mysql 高可用基于 binlog,像主从等系统机制都依赖于 binlog
五、两阶段提交
1.概述
当innodb执行修改时,会经历一个两阶段提交的过程:
- 执行器根据sql写入新数据,然后新数据更新到内存里
- 将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
- 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成
那么如果事务提交过程中出现了异常,数据库崩溃了,就会有以下几种情况:
写 binlog 前崩溃了:对于时刻A,redo log 还是 prepare,binlog 没写,此时崩溃后事务回滚。
写 binlog 后崩溃了:对于时刻B,redo log 还是 prepare,binlog 已经写了,此时发生崩溃后情况如下:
如果redo log 已经标记为 commit,则提交事务,重做
如果redo log 还是 prepare,则去检查 binlog 记录的对应事务是否存在:
如果存在,就提交事务,重做
如果不存在,就回滚
2.为什么需要两阶段提交
我们举个反例,说明一下他的必要性。假设我们要更新某条数据的A字段由0变为2:
- 先 redo log 再 binlog,服务挂了:由于 redo log 还在,可以通过 redo log 恢复数据,A此时是2。但是如果后面要通过 binlog 恢复数据时,由于 binlog 中没有这次修改的记录,恢复后的数据库/备份库就会变为0,丢失了这次更新。
- 先 binlog 再 redo log,服务挂了:由于 redo log 没记录这次更新,所以恢复后这次事务无效,A此时是0。但是 binlog 已经有了“A从0变成2这个记录”,所以恢复以后等于多了一次事务。
之所以这样做,归根结底是为了保证数据库事务的一致性:
因为不管是从库或者备份库都需要通过读取 binlog 来同步数据,所以为了保证保证和主库数据一致,binlog 里记录的每一条事务就必须是已经提交了的,也就是一定要保证往 binlog 里写入数据以后事务不能回滚。
六、总结
- redo log保证更新不丢失,支持的是事务的持久性
- undo log保证事务不成功可以回滚,支持的是事务的原子性
- redo log和binlog的二次提交机制,为事务的一致性提供了一定的保证