MySQL实战45讲——15“redo log与bin log答疑“总结

问题1:为什么要搞2套日志?

1. redolog只有InnoDB有,别的引擎没有;

2. redolog是循环写的,历史已执行的会被覆盖,不全量持久保存,因此binlog的“归档”这个功能,redolog是不具备的;

问题2:binlog与redolog区别?

1. redolog是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用;

2. redolog是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1”;

3. redolog是循环写的,空间固定会用完;binlog是可以追加写入的,即文件写到一定大小后会切换到下一个文件,历史日志不会被覆盖;

问题3:什么是两阶段提交?

可见,redolog的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交";


问题4:在两阶段提交的情况下,是怎么实现崩溃恢复的呢?

基于问题6,可知:二者通过XID就关联起来了;崩溃恢复的情况:

  1. 如果在写入redolog之前崩溃,那么此时redolog与binlog中都没有,是一致的情况,崩溃也无所谓;
  2. 如果在写入redolog prepare阶段后立马崩溃(时刻A),之后会在崩恢复时,由于redolog没有被标记为commit,于是拿着redolog中的XID去binlog中查找,此时肯定是找不到的,那么执行回滚操作;
  3. 如果在写入binlog后,redolog还没commit前发生crash(时刻B),判断规则:如果redo log里面的事务是完整的, 也就是已经有了commit标识, 则直接提交;如果redo log里面的事务只有完整的prepare, 则判断对应的事务binlog是否存在并完整:a. 如果是, 则提交事务;
    b. 否则, 回滚事务。时刻B属于a情况,但对应的事务binlog是否存在并完整怎么判断的呢?

问题5:MySQL怎么知道binlog是完整的?

回答: 一个事务的binlog是有完整格式的:
statement格式的binlog, 最后会有COMMIT;
row格式的binlog, 最后会有一个XID event。
另外, 在MySQL 5.6.2版本以后, 还引入了binlog-checksum参数, 用来验证binlog内容的正确
性。 对于binlog日志由于磁盘原因, 可能会在日志中间出错的情况, MySQL可以通过校验
checksum的结果来发现。 所以, MySQL还是有办法验证事务binlog的完整性的。
 

问题6:redolog和binlog是怎么关联起来的?

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

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

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

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

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

回答: 其实, 两阶段提交是经典的分布式系统问题, 并不是MySQL独有的。
如果必须要举一个场景, 来说明这么做的必要性的话, 那就是事务的持久性问题。
对于InnoDB引擎来说, 如果redo log提交完成了, 事务就不能回滚(如果这还允许回滚, 就可能
覆盖掉别的事务的更新) 。 而如果redo log直接提交, 然后binlog写入的时候失败, InnoDB又回
滚不了, 数据和binlog日志又不一致了。
两阶段提交就是为了给所有人一个机会, 当每个人都说“我ok”的时候, 再一起提交。
 

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

不可以;binlog不具备崩溃恢复crash-safe能力,没法做数据的恢复;因为:

首先要清楚,InnoDB引擎使用的是WAL技术,执行事务的时候,写完内存和日志,事务就算完成了;内存中的脏页会有后台任务根据一些机制刷入磁盘;

如果发生崩溃,要依赖于日志来恢复数据页;但是,binlog没法用来恢复内存中的数据页;

  1. 第一点,binlog是逻辑日志,记录的是这个语句的原始逻辑,不能直接应用于内存;
  2. 第二点,重放binlog的时候,你不知道哪些日志对应的修改已经写入磁盘,也就是虽然binlog拥有全量的日志,但没有一个标志让innoDB判断哪些数据已经刷盘,哪些数据还没有;

恰恰以上两点,redolog都具备,

  1. 第一点,redolog是物理日志,记录的是“在某个数据页上做了什么修改”,可以用来恢复内存;
  2. 第二点,redolog的循环写特性就天然的具备"判断对数据的修改是否落盘"的标志——已经刷入磁盘的数据都会从redolog删除(checkpoint移动);

只用binlog来实现崩溃恢复的流程, 我画了一张示意图, 这里就没有redo log了。

这样的流程下, binlog还是不能支持崩溃恢复的。 我说一个不支持的点吧: binlog没有能力恢
复“数据页”。

如果在图中标的位置, 也就是binlog2写完了, 但是整个事务还没有commit的时候, MySQL发生
了crash。
重启后, 引擎内部事务2会回滚, 然后应用binlog2可以补回来; 但是对于事务1来说, 系统已经
认为提交完成了, 不会再应用一次binlog1。
但是, InnoDB引擎使用的是WAL技术, 执行事务的时候, 写完内存和日志, 事务就算完成了。
如果之后崩溃, 要依赖于日志来恢复数据页。
也就是说在图中这个位置发生崩溃的话, 事务1也是可能丢失了的, 而且是数据页级的丢失。 此
时, binlog里面并没有记录数据页的更新细节, 是补不回来的。

你可能会想,优化一下binlog的内容,让它来记录数据页的更改可以吗?但,这其实就是又做了一个redolog出来;至少现在的binlog能力,还不能支持崩溃恢复;

问题10:那能不能反过来,只用redolog,不要binlog?

如果只从崩溃恢复的角度来讲是可以的;你可以把binlog关掉,这样就没有两阶段提交了,但系统依然是crash-safe的;

但是,如果你了解一下业界各个公司的使用场景的话,就会发现在正式的生产库上,binlog都是开着的;因为binlog有着redolog无法替代的功能;

  1. redolog不能用来归档;redolog是循环写,写到末尾是要回到开头继续写的;这样历史日志没法保留,redolog也就起不到归档的作用;
  2. MySQL系统依赖于binlog;binlog作为MySQL一开始就有的功能,被用在了很多地方;其中,MySQL系统高可用的基础,就是binlog复制;还有很多公司有异构系统(比如一些数据分析系统),这些系统就靠消费MySQL的binlog来更新自己的数据;关掉binlog的话,这些下游系统就没法输入了;

总之,由于现在包括MySQL高可用在内的很多系统机制都依赖于binlog,所以“鸠占鹊巢”redolog还做不到;

问题11: redo log一般设置多大?

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


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

回答: 这个问题其实问得非常好。 这里涉及到了, “redo log里面到底是什么”的问题。
实际上, redo log并没有记录数据页的完整数据, 所以它并没有能力自己去更新磁盘数据页, 也
就不存在“数据最终落盘, 是由redo log更新过去”的情况。

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

问题13: 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语句的时候做的.

这里说的是事务执行过程中不会“主动去刷盘”, 以减少不必要的IO消耗。 但是可能会出现“被
动写入磁盘”, 比如内存不够、 其他事务提交等情况。 这个问题我们会在后面第22篇文章
《MySQL有哪些“饮鸩止渴”的提高性能的方法? 》 中再详细展开) 。
单独执行一个更新语句的时候, InnoDB会自己启动一个事务, 在语句执行完成的时候提交。 过
程跟上面是一样的, 只不过是“压缩”到了一个语句里面完成。
以上这些问题, 就是把大家提过的关于redo log和binlog的问题串起来, 做的一次集中回答。 如
果你还有问题, 可以在评论区继续留言补充。
 


————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/minghao0508/article/details/127763412

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值