本篇文章给大家带来的内容是关于MySQL中常见的日志问题介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
MySQL 里有两个日志,即:重做日志(redo log)和归档日志(binlog)。(推荐课程:MySQL教程)
其中,binlog 可以给备库使用,也可以保存起来用于恢复数据库历史数据。它是实现在 server 层的,所有引擎可以共用。redo log 是 InnoDB 特有的日志,用来支持 crash-safe 能力。
你一定听过 MySQL 事务的两阶段提交,指的就是在事务提交的时候,分成 prepare 和 commit 两个阶段。
如图 1 所示为一个事务的执行流程,你在最后三步可以看到,redo log 先 prepare 完成,再写 binlog,最后才进入 redo log commit 阶段。
图 1 两阶段提交示意图
这里,我要先和你解释一个误会式的问题:这个图不就是一个 update 语句的执行流程吗,怎么还会调用 commit 语句?
通常情况下,你会产生这个疑问的原因,在于把两个“commit”的概念混淆了:
问题中的“commit 语句”,是指 MySQL 语法中,用于提交一个事务的命令。一般跟 begin/start transaction 配对使用。
而我们图中用到的这个“commit 步骤”,指的是事务提交过程中的一个小步骤,也是最后一步。当这个步骤执行完成后,这个事务就提交完成了。
“commit 语句”执行的时候,会包含“commit 步骤”。
而我们这个例子里面,没有显式地开启事务,因此这个 update 语句自己就是一个事务,在执行完成后提交事务时,就会用到这个“commit 步骤”。
接下来,我们就一起分析一下在两阶段提交的不同时刻,MySQL 异常重启会出现什么现象。
如果在图中时刻 A 的地方,也就是写入 redo log 处于 prepare 阶段之后、写 binlog 之前,发生了崩溃(crash),由于此时 binlog 还没写,redo log 也还没提交,所以崩溃恢复的时候,这个事务会回滚。这时候,binlog 还没写,所以也不会传到备库。到这里,我们都可以理解。
而我们理解会出现问题的地方,主要集中在时刻 B,也就是 binlog 写完,redo log 还没 commit 前发生 crash,那崩溃恢复的时候 MySQL 会怎么处理?
我们先来看一下崩溃恢复时的判断规则。
1、如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
2、如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:
a.如果是,则提交事务;
b.否则,回滚事务。
这里,时刻 B 发生 crash 对应的就是 2(a) 的情况,崩溃恢复过程中事务会被提交。
现在,我们就针对两阶段提交再继续延展一下。
问题 1:MySQL 怎么知道 binlog 是完整的?
回答:一个事务的 binlog 是有完整格式的:
如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
问题 3:处于 prepare 阶段的 redo log 加上完整 binlog,重启就能恢复,MySQL 为什么要这么设计?
回答:其实,这个问题还是跟我们在反证法中说到的数据与备份的一致性有关。在时刻 B,也就是 binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就来源gaodai#ma#com搞@代~码$网会被从库(或者用这个 binlog 恢复出来的库)使用。
所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。