第十五章 日志和索引相关问题

第十五章 日志和索引相关问题

日志相关问题

两阶段提交示意图

Untitled

如果崩溃发生在时刻 A

  • 如果在图中时刻 A 的地方,也就是写入 redo log 处于 prepare 阶段之后、写 binlog 之前,发生了崩溃(crash),由于此时 binlog 还没写,redo log 也还没提交,所以崩溃恢复的时候,这个事务会回滚。
  • 这时候,binlog 还没写,所以也不会传到备库。

如果崩溃发生在时刻 B

崩溃恢复时的判断规则:

  1. 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交
  2. 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并且是否完整:
    1. 如果是,则提交事务
    2. 否则,回滚事务
  3. 时刻 B 发生 crash 对应的就是 2(a) 的情况,崩溃恢复过程中事务会被提交

MySQL 怎么知道 binlog 是完整的?

  • 一个事务的 binlog 是有完整格式的:
    • statement 格式的 binlog,最后会有 COMMIT
    • row 格式的 binlog,最后会有一个 XID event
  • binlog 日志的完整性是由 checksum 保证,这样子可以确保 binlog 数据是完整的

redo log 和 binlog 是怎么关联起来的?

  • 它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:
    • 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交
    • 如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务

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

  • 其实,这个问题还是跟我们在反证法中说到的数据与备份的一致性有关
  • 在时刻 B,也就是 binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就会被从库(或者用这个 binlog 恢复出来的库)使用
  • 所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性
  • 其实说白了,两个日志都有自己的用处,所以我们设计出两阶段提交,保证两种日志的数据一致性,只要两种日志的数据是一致的,就可以提交事务

如果这样的话,为什么还要两阶段提交呢?干脆先 redo log 写完,再写 binlog。崩溃恢复的时候,必须得两个日志都完整才可以。是不是一样的逻辑?

  • 两阶段提交是经典的分布式系统问题,并不是 MySQL 独有的
  • 其实把 MySQL 的两阶段提交也可以看成两个分布式服务处理两个不同事情
  • redo log 是在 InnoDB 引擎内操作的,binlog 是在 server 层操作的
  • 我们就可以把 引擎层 和 server 层看作两个分布式服务,那他们要分别进行两个相关联的操作,就意味着要实现分布式事务,而两阶段提交,就是其中的一种解决方案

举一个例子:

对于 InnoDB 引擎来说,如果 redo log 提交完成了,事务就不能回滚(如果这还允许回滚,就可能覆盖掉别的事务的更新)

而如果 redo log 直接提交,然后 binlog 写入的时候失败,InnoDB 又回滚不了,数据和 binlog 日志又不一致了。

两阶段提交就是为了给所有人一个机会,当每个人都说 “我 ok” 的时候,再一起提交。

不引入两个日志,也就没有两阶段提交的必要了。只用 binlog 来支持崩溃恢复,又能支持归档,不就可以了?

  • 不可以
  • 我认为还是因为 binlog 是追加写的,无法确定从哪个点开始的数据是已经刷盘了
  • redo log 只要在 checkpoint 后面的肯定是没有刷盘的,所以只需要重放一遍即可
  • 如果 binlog 也能准确知道是从哪个点开始数据没有刷盘的,那么也可以像 redo log 一样重放一遍即可

那能不能反过来,只用 redo log,不要 binlog?

如果只从崩溃恢复的角度来讲是可以的。

你可以把 binlog 关掉,这样就没有两阶段提交了,但系统依然是 crash-safe 的

但是 binlog 有着 redo log 无法替代的功能:

  1. 归档:redo log 是循环写,写到末尾是要回到开头继续写的。这样历史日志没法保留,redo log 也就起不到归档的作用
  2. MySQL 系统依赖于 binlogbinlog 作为 MySQL 一开始就有的功能,被用在了很多地方。其中,MySQL 系统高可用的基础,就是 binlog 复制

redo log 一般设置多大?

  • redo log 太小的话,会导致很快就被写满,然后不得不强行刷 redo log,这样 WAL 机制的能力就发挥不出来了
  • 如果是现在常见的几个 TB 的磁盘的话,就不要太小气了,直接将 redo log 设置为 4 个文件、每个文件 1GB 吧

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

redo log 里面到底是什么 ?

  • redo log 并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也就不存在“数据最终落盘,是由 redo log 更新过去”的情况
  • redo log 记录"在某个数据页上做了什么修改",而不是"这个数据修改后最新值为什么"
  • 因此是需要先把磁盘的数据读入内存再执行 redo log 中的内容的

有一个脏页要刷了是吧,现在的状态是:

  • 内存中的数据页是脏页
  • 脏页中的数据已经记录在 redo log 中了
  • 磁盘文件中的数据页是旧的

刷脏页的时候发生了什么:

  • 脏页直接写盘
  • 此时 内存中的数据页、硬盘中的数据页、redo log 中对应的内容,一致

擦除 redo log 的时候发生了什么:

  • 如果要擦的 redo log 中的内容,对应到内存中的数据页不是脏页,直接擦
  • 如果要擦的 redo log 中的内容,对应到内存中的数据页是脏页,脏页刷盘,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 文件(文件名是 ib_logfile+ 数字),是在执行 commit 语句的时候做的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿小羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值