前文《MySQL实战45讲》——学习笔记01-03 “MySQL基本架构、日志系统、事务隔离“介绍了MySQL的日志系统,包括redo log和bin log以及两阶段提交,本篇作为该内容的补充,进一步解释MySQL中的这两套日志的关系;
1. redolog只有InnoDB有,别的引擎没有;
2. redolog是循环写的,历史已执行的会被覆盖,不全量持久保存,因此binlog的“归档”这个功能,redolog是不具备的;
1. redolog是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用;
2. redolog是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1”;
3. redolog是循环写的,空间固定会用完;binlog是可以追加写入的,即文件写到一定大小后会切换到下一个文件,历史日志不会被覆盖;
先看[执行器](绿色)与[InnoDB引擎](红色)是如何更新一条指定的数据的:
可见,redolog的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交";
不可以;binlog不具备崩溃恢复crash-safe能力,没法做数据的恢复;因为:
首先要清楚,InnoDB引擎使用的是WAL技术,执行事务的时候,写完内存和日志,事务就算完成了;内存中的脏页会有后台任务根据一些机制刷入磁盘;
如果发生崩溃,要依赖于日志来恢复数据页;但是,binlog没法用来恢复内存中的数据页;
(1)第一点,binlog是逻辑日志,记录的是这个语句的原始逻辑,不能直接应用于内存;
(2)第二点,重放binlog的时候,你不知道哪些日志对应的修改已经写入磁盘,也就是虽然binlog拥有全量的日志,但没有一个标志让innoDB判断哪些数据已经刷盘,哪些数据还没有;
恰恰以上两点,redolog都具备,
(1)第一点,redolog是物理日志,记录的是“在某个数据页上做了什么修改”,可以用来恢复内存;
(2)第二点,redolog的循环写特性就天然的具备"判断对数据的修改是否落盘"的标志——已经刷入磁盘的数据都会从redolog删除(checkpoint移动);
你可能会想,优化一下binlog的内容,让它来记录数据页的更改可以吗?但,这其实就是又做了一个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还做不到;
它们有一个共同的数据字段,叫XID;在写入redolog时,会顺便记录XID,即当前事务id;在写入binlog时,也会写入XID;
基于问题6,可知:二者通过XID就关联起来了;崩溃恢复的情况:
(1)如果在写入redolog之前崩溃,那么此时redolog与binlog中都没有,是一致的情况,崩溃也无所谓;
(2)如果在写入redolog prepare阶段后立马崩溃,之后会在崩恢复时,由于redolog没有被标记为commit,于是拿着redolog中的XID去binlog中查找,此时肯定是找不到的,那么执行回滚操作;
(3)如果在写入binlog后立马崩溃,在恢复时,由redolog中的XID可以找到对应的binlog,这个时候直接提交即可;
问题主要是跟数据与备份的一致性有关;
如果binlog写完以后MySQL发生崩溃,这时候binlog已经写入了,之后就会被从库(或者用这个binlog恢复出来的库)使用;
所以,在主库上也要提交这个事务;采用这个策略,主库和备库的数据就保证了一致性;
两阶段提交是经典的分布式系统问题,并不是MySQL独有的;
redolog是在Innodb引擎内操作的,而binlog是在server层操作的,我们就可以把引擎层和server层看成两个分布式服务,那他们要分别进行两个相关联的操作,就意味着要实现分布式事务,而两阶段提交,就是其中的一种解决方案;
因为这两个日志属于MYSQL的不同层,因此还是会存在一个成功一个失败的情况,就像分布式事务中不同分布式系统操作的数据源不同,并且可靠性也不同,就一定需要考虑A成功B失败的情况;
有个前提——对于InnoDB引擎来说,如果redolog提交完成了,事务就不能回滚(如果这都commit了还允许回滚,就可能覆盖掉别的事务的更新);
因此,如果不用两阶段,redolog直接提交,然后binlog写入的时候失败,这种情况下InnoDB又回滚不了,那结果就是主库数据和binlog日志又不一致了,肯定是不行的,最起码的问题就是主从就不一致了;
两阶段提交也可以理解成给每个系统一个确认的机会,当每个人都说“我ok”的时候,再一起提交,如果前置就发现某个系统还没准备好时,就不用提交了;