第十五讲——日志答疑

在两阶段提交的不同瞬间,MySQL 如果发生异常是怎么保证数据完整性的?

两阶段提交示意图

这是个 update 语句的执行流程吗,怎么会调用 commit语句?

这里其实有**两个“commit”**的概念:

  • 平常说的“commit语句”,是指 MySQL 语法中,用于提交一个事务的命令。一般跟 begin/start transaction 配对使用。
  • 而这个图中是“commit步骤”,指的是事务提交过程中的一个小步骤,也是最后一步。当这个步骤执行完了,这个事物就提交了。
  • “commit语句”执行时也包含“commit步骤”。

因为 update 语句本身自己就是个事务,所以就会有“commit步骤”。

在两阶段提交的不同时刻,MySQL 异常重启会出现什么现象

在图中 A 时刻,写入 redo log 处于 prepare 阶段之后、写 binlog 之前,发生了崩溃(crash)。由于 binlog 没写,redo log 也没提交,所以数据库恢复之后,这个事务会回滚。

在 B 时刻,binlog 写完,但 redo log 没 commit 之前 crash,MySQL 会怎么办呢?

我们来看一下崩溃恢复规则:

  • 如果 redo log 中的事务是完整的,也就是有了 commit 标识,则直接提交。
  • 如果 redo log 中的事务只有完整的 prepare,则判断对应的 benlog 是否存在并完整:
    • 如果是,则提交事务;
    • 如果不是,则回滚事务。

此时 B 时刻就是符合 redo log 有完整的 prepare,binlog 完整,所以直接提交事务。

追问一:MySQL 怎么知道 binlog 是完整的?

回答:一个事务是有着完整格式的。

  • statement 格式的 binlog,最后会有 COMMIT;
  • row 格式的 binlog,最后会有一个 XID event。

另外,在 MySQL 5.6.2 版本之后,引入了 binlog-checksum 用来校验 binlog的完整性。

追问二:redo log 和 binlog 是怎么关联起来的?

回答:它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redolog:

  • 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交。
  • 如果只有prepare时,就拿着 XID 去 binlog 找对应的事务。

追问三:处于 prepare 阶段的 redo log 加上完整 binlog,重启就能恢 复,MySQL 为什么要这么设计?

回答:其实,这个问题还是跟我们在反证法中说到的数据与备份的一致性有关。在时刻 B,也就是 binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就会被从库(或者用这个 binlog恢复出来的库)使用。

所以,在主库上也要提交这个事务,保证主从一致。

追问4:先redo log写完,再写 binlog。崩溃恢复的时候,必须得两个日志都完整才可以。是不是一样的逻辑?

回答:其实,两阶段提交是经典的分布式系统问题。

如果必须要举一个场景,来说明这么做的必要性的话,那就是事务的持久性问题。

对于 InnoDB 引擎来说,如果 redo log 提交完成了,事务就不能回滚(如果这还允许回滚,就可能覆盖掉别的事务的更新)。而如果 redo log 直接提交,然后 binlog 写入的时候失败,InnoDB 又回滚不了,数据和 binlog 日志又不一致了。 两阶段提交就是为了给所有人一个机会,当每个人都说“我ok”的时候,再一起提交。

追问5:只用 binlog 来支持崩溃恢复,又能支持归档,可以吗?

回答:不可以。

历史原因中,MySQL 原生引擎 MyISAM,在设计之初就没有支持崩溃恢复。

InnoDB 在加入 MySQL 之前就已经提供了崩溃恢复和事务支持。在接入 MySQL 之后,就直接用 InnoDB 的 redo log 来提供崩溃恢复服务了。

实际上的话,只用 binlog 还是不能支持崩溃恢复,binlog 没有能力恢复“数据页”

在上图中,binlog2 写完了还没提交,系统发生 crash。重启后,事务 2 会回滚,但是对于事务 1 来说,系统认为提交完成了,不会在应用一次事务 1。

而且 InnoDB 使用的是 WAL(Write-ahead logging,预写日志),执行事务时,写完内存和日志,事务就算完成了。如果之后崩溃,要依赖于日志来恢复数据页。

也就是说,在图中这个位置崩溃,事务 1 也可能丢失的,而且是数据页级的丢失。此时,binlog 里面没有数据页的更新细节,是补不回来的。

追问六:那只用 redo log,不用 binlog 呢?

回答:如果只从崩溃的角度讲,可以关闭 binlog,这样就没有两阶段提交,但系统还是 crash-safe。

但 binlog 有 redo log 替代不了的功能:

  • 归档。redo log 是循环写,这样历史日志没法保留,只靠 redo log 无法归档。
  • MySQL 系统依赖于 binlog,被用在很多地方。其中,MySQL 高可用的基础就是 binlog 复制。

追问七:redo log 一般设置多大?

回答:如果是常见的几 TB 磁盘的话,直接设置为 4 个文件、每个文件 1GB。

追问八:正常运行中的实例,数据写入后的最终落盘,是从 redo log 更新过来的还是从 buffer pool 更新过来的呢?

回答:实际上 redolog 并没有记录数据页的完整信息,所以它没有能力自己去更新磁盘数据。

  1. 如果系统正常运行的话,就是把脏页直接写入磁盘。这个过程,甚至与 redo log 没有一点关系。
  2. 在崩溃恢复场景中,InnoDB 判断一个页可能在 crash 时丢失了数据,就会将数据页读入内存,让后让 redo log 更新内存内容。更新完后,内存中的变成了脏页,就和第一种情况一样了。

追问九:redo log buffer 是什么?是先修改内存,还是先写redo log文件?

在一个事务的更新中,日志是要写多次的。比如:

begin;
insert into t1 ...
insert into t2 ...
commit;

这两条插入语句生成的日志要保存起来,但是又不能在还没 commit 的时候就直接写到 redo log 文件里。

所以,redo log buffer就是一块内存,用来先存 redo 日志的。在执行第一个 insert 的时候,数据的内存被修改了,redo log buffer 也写入的日志。

真正把日志写入到 redo log 文件中,是上面 commit 语句执行的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值