参考文章:https://time.geekbang.org/column/article/68633
目录
redo log(重做日志)
binlog(归档日志)
怎样让数据库恢复到半个月内任意一秒的状态?
两阶段提交
创建表:mysql> create table T(ID int primary key, c int);
更新数据:mysql> update T set c=c+1 where ID=2;
执行流程:连接器连接数据库-->缓存结果清空-->分析器会通过词法和语法解析知道这是一条更新语句-->优化器决定要使用 ID 这个索引-->执行器负责具体执行,找到这一行,然后更新
与查询过程的区别在于:更新流程还涉及两个重要的日志模块,它们正是我们今天要讨论的主角:redo log(重做日志)和 binlog(归档日志)。
redo log(重做日志)
WAL 技术(Write-Ahead Logging):它的关键点就是先写日志,再写磁盘。具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。
InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么redo log 总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。
checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint 之间的是redo log上还空着的部分,可以用来记录新的操作。
如果 write pos 追上 checkpoint,表示redo log满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
binlog(归档日志)
redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)
我想你肯定会问,为什么会有两份日志呢?
因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
这两种日志的区别:
1、redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
2、redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
3、redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
给出这个 update 语句的执行流程图,图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。
怎样让数据库恢复到半个月内任意一秒的状态?
当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:
1、首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
2、然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻
两阶段提交
为什么日志需要“两阶段提交”。这里不妨用反证法来进行解释。
由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。
1、先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
2、先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
帮助理解:
-----------------------------------我是快乐且好学的分割线-----------------------------------
小结:
redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。
sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。
与 MySQL 日志系统密切相关的“两阶段提交”。两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案
-----------------------------------我是快乐且好学的分割线-----------------------------------
思考:
定期全量备份的周期“取决于系统重要性,有的是一天一备,有的是一周一备”。那么在什么场景下,一天一备会比一周一备更有优势呢?或者说,它影响了这个数据库系统的哪个指标?
答案:一天一备跟一周一备的对比。好处是“最长恢复时间”更短。
在一天一备的模式里,最坏情况下需要应用一天的 binlog。比如,你每天 0 点做一次全量备份,而要恢复出一个到昨天晚上 23 点的备份。
一周一备最坏情况就要应用一周的 binlog 了。系统的对应指标就是 RTO(恢复目标时间)。当然这个是有成本的,因为更频繁全量备份需要消耗更多存储空间,所以这个 RTO 是成本换来的,就需要你根据业务重要性来评估了。
-----------------------------------我是快乐且好学的分割线-----------------------------------
有价值的问题:
1、 Redo log不是记录数据页“更新之后的状态”,而是记录这个页 “做了什么改动”。Binlog有两种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有。
2、问:感觉binlog很多余,按理是不是只要redo log就够了?
答:binlog还不能去掉。一个原因是,redolog只有InnoDB有,别的引擎没有。另一个原因是,redolog是循环写的,不持久保存,binlog的“归档”这个功能,redolog是不具备的。
3、Bin log 用于记录了完整的逻辑记录,所有的逻辑记录在 bin log 里都能找到,所以在备份恢复时,是以 bin log 为基础,通过其记录的完整逻辑操作,备份出一个和原库完整的数据。
因为是两阶段提交,这时候redolog只是完成了prepare, 而binlog又失败,那么事务本身会回滚,所以这个库里面status的值是0。如果通过binlog 恢复出一个库,status值也是0。这样不算丢失,这样是合理的结果。
4、问:如果在非常极端的情况下,redo log被写满,而redo log涉及的事务均未提交,此时又有新的事务进来时,就要擦除redo log,这就意味着被修改的的脏页此时要被迫被flush到磁盘了,因为用来保证事务持久性的redo log就要消失了。但如若真的执行了这样的操作,数据就在被commit之前被持久化到磁盘中了。当真的遇到这样的恶劣情况时,mysql会如何处理呢,会直接报错吗?还是有什么应对的方法和策略呢?
答:这些数据在内存中是无效其他事务读不到的(读到了也放弃),同样的,即使写进磁盘,也没关系,再次读到内存以后,还是原来的逻辑
-----------------------------------我是快乐且好学的分割线-----------------------------------
自问自答:
为该讲的内容总结了几个问题, 大家复习的时候可以先尝试回答这些问题检查自己的掌握程度:
1. redo log的概念是什么? 为什么会存在.
2. 什么是WAL(write-ahead log)机制, 好处是什么.
3. redo log 为什么可以保证crash safe机制.
4. binlog的概念是什么, 起到什么作用, 可以做crash safe吗?
5. binlog和redolog的不同点有哪些?
6. 物理一致性和逻辑一致性各应该怎么理解?
7. 执行器和innoDB在执行update语句时候的流程是什么样的?
8. 如果数据库误操作, 如何执行数据恢复?
9. 什么是两阶段提交, 为什么需要两阶段提交, 两阶段提交怎么保证数据库中两份日志间的逻辑一致性(什么叫逻辑一致性)?
10. 如果不是两阶段提交, 先写redo log和先写bin log两种情况各会遇到什么问题?