第二十一节、问题答疑

2020-06-17  本文已影响0人  小母牛不生产奶

1、在两阶段提交的不同瞬间,MySQL如果发生异常重启,是专门保证数据完整性的?

事务两阶段提交

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

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

2、如果在时刻B,也就是binlog写完,redo log还没有commit前发生crash,那崩溃恢复的时候MySQL会怎么处理:

    2.1、如果redo log里面的事务时完整的,也就是已经有了commit标识,则直接提交;

    2.2、如果redo log里面的事务只有完整的prepare,则判断对应的事务binlog是否存在并完整:

        a、如果是,则提交事务;

        b、否则,回滚事务。

这里,时刻B发生crash对应的就是2.1(a)的情况,崩溃恢复过程中事务会被提交。


2、MySQL怎么知道binlog是完整的?

一个事务的binlog是有完整格式的:

statement格式的binlog,最后会有COMMIT;

row格式的binlog,最后会有一个XID event。

另外,在MySQL5.6.2版本以后,还引入了binlog-checksum参数,用来验证binlog内容的正确性。对于binlog日志由于磁盘原因,可能会在日志中间出错的情况,MySQL可以通过校验checksum的结果来发现。所以,MySQL还是有办法验证事务binlog的完整性的。


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

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

如果碰到既有prepare、又有ccommit的redo log,就直接提交;

如果碰到只有prepare 、而没有commit的redo log,就拿着XID去binlog找对应的事务。


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

在时刻B,也就是binlog写完以后MySQL发生崩溃,这时候binlog已经写入了,之后就会被从库(或者用这个binlog恢复出来的库)使用。所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。


5、myslq为什么需要两阶段提交尼?先redo log写完,再写binlog。崩溃恢复的时候,必须两个日志都完整才可以。是不是一样 的逻辑?

两阶段提交时经典的分布式系统问题,并不是mysql独有的。

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

对于InnoDB引擎来说,如果redo log提交完成了,事务就不能回滚(如果这还允许回滚,就可能覆盖掉别的事务的更新)。而如果redo log直接提交,然后binlog写入的时候失败,InnoDB又回滚不了,数据和binlog日志又不一致了。

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


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

这是不可以的,InnoDB在作为MySQL的插件加入MySQL引擎家族之前,就已经是一个提供了崩溃恢复和事务支持的引擎了。InnoDB接入了MySQL后,发现既然binlog没有崩溃恢复的能力,那就用InnoDB原有的redo log好了。

现在binlog能力还不能支持崩溃恢复。


7、能不能只用redo log,不要binlog?

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

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

一个是归档。redo log是循环写的,写到末尾是要回到开头继续写的。这样历史日志没法保留,redo log也就起不到归档的作用。

一个就是MySQL系统依赖binlog。binlog作为MySQL一开始就有的功能,被用在了很多地方。其中,MySQL系统高可用的基础,就是binlog复制。

还有很多公司的异构系统(一些数据分析系统),这些系统就是靠消费MySQL的binlog来更新自己的数据。关掉binlog的话,这些下游系统就没法输入了。

总之,由于现在包括Mysql高可用在内的很多系统机制都依赖于binlog,所以redo log还替代不到。


8、redo log一般设置多大?

redo log太小的话,会导致很快就被写满,然后不得不强行刷redo log,这样WAL机制的能力就发挥不出来了。


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

redo log并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也就不存在”数据最终落盘,是由redo log更新过去“的情况。

1、如果是正常运行的实例的话,数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程,甚至与redo log毫无关系。

2、在崩溃恢复场景中,InnoDB如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让redo log更新内存内容。更新完成后,内存页变成脏页,就回到了第一种情况的状态。


10、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语句的时候做的。

上一篇下一篇

猜你喜欢

热点阅读