15-01 | redo log & binlog日志相关问题(看完后会让整个人清爽了)

问题1:我们从前面知道了MySQL是有两阶段提交的,那么在这两个阶段中发生crash,MySQL怎么保证数据完整性?

两阶段如下:【执行器将要更新的数据调用引擎层接口写入到redo log后标记为prepare阶段(此时记为时刻1),然后告知server端执行器生成这个操作的binlog,并写binlog到磁盘(此时极为时刻2),紧接着执行器告知引擎层提交事务并且通知redo log将prepare改为commit】

分为两阶段判定:

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

  2. 重点是在时刻 B,也就是 binlog 写完,redo log 还没 commit 前发生 crash,那崩溃恢复的时候 MySQL 会怎么处理?也是分两种情况:
    – 如果redo log中事务是完整并且有commit标记,则故障恢复中直接提交
    – 如果redo log中事务只有完整的prepare,则判断对应的事务 binlog 是否存在并完整:完整则提交。不完整则回滚事务。

1.1.binlog是否完整究竟是怎么判定的?
binlog前面说过有三种模式,其中row模式binlog尾部会有XID event标记;statement格式的binlog末尾会有commit。
PS:在 MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。对于 binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。

1.2.redo log和binlog关联是什么?
回答:它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务,进而判断binlog的完整性。

1.3.处于 prepare 阶段的 redo log 加上完整 binlog,重启就能恢复,MySQL 为什么要这么设计?
数据与备份的一致性有关。当备份还原时通过binlog可以还原。

1.4.如果这样的话,干脆先 redo log 写完,再写 binlog。为什么还要两阶段提交呢?崩溃恢复的时候,必须得两个日志都完整才可以。是不是一样的逻辑?
个人理解,Mysql的两阶段提就是两个分布式服务处理两个不同事情,redo log在Innodb引擎内操作的,binlog是在server层操作的,我们就可以把引擎层和server层看成两个分布式服务,如果各自完成各自的事情,对于多系统直接数据一致性很难保障。比如A完成了,通知B,B超时或者挂了,A是继续写还是回滚,回滚要回滚多久的才行,没有一个保证机制。

总结:专题作者一句话就是,两阶段提交就是为了给所有人一个机会,当每个人都说“我 ok”的时候,再一起提交。

1.5.只用 binlog 来支持崩溃恢复,又能支持归档,不就可以了?
即:… -> “数据更新到内存” -> “写 binlog” -> “提交事务”,是不是也可以提供崩溃恢复的能力?
现有流程:
prepare1
binlog1
commit1
prepare2
binlog2(binlog2后崩溃)
commit2
概括:binlog 没有能力恢复“数据页”。redo log是基于数据页的。

举个我自己也没怎么看懂的例子😂:
如:binlog1是update c+1;binlog2是update c+1;现在在binlog2写完没提交的时候发生crash,这时对数据的更新可能还停留在内存中,并未刷盘,crash后内存数据丢失。 由于binlog2事务未完成提交,系统会应用binlog2恢复数据,即此时c+1;但对于binlog1来说,已经完成了事务,系统不会再应用binlog1来恢复数据,所以数据c不会再+1. 这时数据c只加了一次,与未crash前c+了两次不同 即binlog没有能力恢复数据页。。。。。在上述这个位置崩溃的话,事务1可能只是redolog写进磁盘并且提交了,但是事务1更新的记录并没有刷盘,也就是丢失了。 但是恢复的时候我们只用binlog来恢复,这时候事务1显示是提交的,所以不会应用binlog,导致这块数据就丢失了。 因为binlog并不记录数据页级的丢失。 如果真想使用binlog来恢复的话,那么就要在每个commit之前,将更改的内存记录刷盘。刷盘之后再将这个事务改为commit状态。 这样崩溃恢复就可以在事务级去做了,而不用在数据页级去做了。

1.6.只用 redo log,不要 binlog?
redo log只从故障恢复来讲是可以的,但是从数据备份【redo log循环写覆盖】和异构系统【消费binlog】之间的数据交互上是不行的。

1.7.redo log一般设置多大?
太小会导致一直刷盘,这样WAL中顺序写磁盘的有点就发挥不出来,前几节中说到最好分4个文件每个1G

1.8.正常运行中的实例,数据写入后的最终落盘,是从 redo log 更新过来的还是从 buffer pool 更新过来的呢?
从redo log 里面到底是什么角度回答,redo_log记录"在某个数据页上做了什么修改",而不是"这个数据修改后最新值为什么"
官方话术,redo log 并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也就不存在“数据最终落盘,是由 redo log 更新过去”的情况。

  • 如果是正常运行的实例的话,数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程,甚至与 redo log 毫无关系。
  • 在崩溃恢复场景中,InnoDB 如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让 redo log 更新内存内容。更新完成后,内存页变成脏页,就回到了第一种情况的状态。

1.9.redo log buffer 是什么?是先修改内存,还是先写 redo log 文件?(PS:试想一下如果在写内存的时候掉电会怎么样)
在一个事务的更新过程中,日志是要写多次的。比如下面这个事务:

begin;
insert into t1 …
insert into t2 …
commit;

这个事务要往两个表中插入记录,插入数据的过程中,生成的日志都得先保存起来,但又不能在还没 commit 的时候就直接写到 redo log 文件里。所以,redo log buffer 就是一块内存,用来先存 redo 日志的。
也就是说,在执行第一个 insert 的时候,数据的内存被修改了,redo log buffer 也写入了日志。但是,真正把日志写到 redo log 文件(文件名是 ib_logfile+ 数字),是在执行 commit 语句的时候做的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值