MySQL 日志系统
一. redo Log
- redo log 是 InnoDB 引擎特有的功能,是物理日志。
- 当 MySQL 执行更新操作时,InnoDB 引擎就会先把记录写到 redo log 里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做的。
- InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么整个 redo log 就可以保存 4G 的数据。redo log 是每次从头开始写,写到末尾就又回到开头循环写。
- write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
- write pos 和 checkpoint 之间的是 redo log 上可写的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示 redo log 已满,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。此时就需要把这部分 redo log 对应的内存脏页写入磁盘中。
- 有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
二. binlog
- binlog 是 MySQL server 层提供的日志。
- binlog 有两种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有。
- binlog 和 redo log 的不同之处:
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
- redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
- 即使有了 redo log 来做 crash-safe,但是 binlog 仍然是必不可少的,原因如下:
- redo log 是 InnoDB 引擎所独有的,如果使用其他引擎就无能为力了;
- redo log 是循环写的,大小有限制,因此无法保存全量数据,并不具备 binlog 的归档能力。
三. 更新操作执行流程
以下面这条SQL语句为例:
update T set c=c+1 where ID=2;
四. 两阶段提交
通过上述流程可以很明显看出,更新操作采用了两阶段提交算法,主要是为了保证 redo log 和 binlog 的数据一致性。两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案。
五. MySQL 的崩溃恢复规则
MySQL 主要依赖 redo log 进行崩溃后的数据恢复。redo log 和 binlog 有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log。如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
恢复崩溃规则如下:
- 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
- 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:
a. 如果是,则提交事务;
b. 否则,回滚事务。
.