一、日志相关问题:
1、在两阶段提交的不同瞬间,MySQL如果发生异常重启,是怎么保证数据完整性的?
如果在写入redo log 处于prepare阶段之后、写binlog之前,发生了崩溃(crash),由于此时binlog还没写,redo log也还没提交,所以崩溃恢复的时候,这个事务会回滚。这时候,binlog还没写,所以也不会传到备库。
如果在binlog写完,redo log还没commit前发生crash,则崩溃恢复过程中事务会被提交。
2、崩溃恢复时的判断规则。
如果redo log里面的事务是完整的,也就是已经有了commit标识,则直接提交;
如果redo log里面的事务只有完整的prepare,则判断对应的事务binlog是否存在并完整:
a. 如果是,则提交事务;
b. 否则,回滚事务。
3、MySQL怎么判断binlog是否完整?
一个事务的binlog是有完整格式的:
statement格式的binlog,最后会有COMMIT;
row格式的binlog,最后会有一个XID event。
另外,在MySQL 5.6.2版本以后,还引入了binlog-checksum参数,用来验证binlog内容的正确性。对于binlog日志由于磁盘原因,可能会在日志中间出错的情况,MySQL可以通过校验checksum的结果来发现。所以,MySQL还是有办法验证事务binlog的完整性的。
4、redo log 和 binlog是怎么关联起来的?
它们有一个共同的数据字段,叫XID。崩溃恢复的时候,会按顺序扫描redo log:
如果碰到既有prepare、又有commit的redo log,就直接提交;
如果碰到只有parepare、而没有commit的redo log,就拿着XID去binlog找对应的事务。
5、处于prepare阶段的redo log加上完整binlog,重启就能恢复,MySQL为什么要这么设计?
采用这个策略,保证了主库和备库的数据的一致性。
6、处于prepare阶段的redo log加上完整binlog,重启就能恢复,为什么还要两阶段提交呢?干脆先redo log写完,再写binlog。崩溃恢复的时候,必须得两个日志都完整才可以。是不是一样的逻辑?
其实,两阶段提交是经典的分布式系统问题,并不是MySQL独有的。如果必须要举一个场景,来说明这么做的必要性的话,那就是事务的持久性问题。
对于InnoDB引擎来说,如果redo log提交完成了,事务就不能回滚(如果这还允许回滚,就可能覆盖掉别的事务的更新)。而如果redo log直接提交,然后binlog写入的时候失败,InnoDB又回滚不了,数据和binlog日志又不一致了。两阶段提交就是为了给所有人一个机会,当每个人都说“我ok”的时候,再一起提交。
7、redo log一般设置多大?
redo log太小的话,会导致很快就被写满,然后不得不强行刷redo log,这样WAL机制的能力就发挥不出来了。所以,如果是现在常见的几个TB的磁盘的话,可以直接将redo log设置为4个文件、每个文件1GB。
8、正常运行中的实例,数据写入后的最终落盘,是从redo log更新过来的还是从buffer pool更新过来的呢?
redo log并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也就不存在“数据最终落盘,是由redo log更新过去”的情况。
如果是正常运行的实例的话,数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程,甚至与redo log毫无关系。
在崩溃恢复场景中,InnoDB如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让redo log更新内存内容。更新完成后,内存页变成脏页,就回到了第一种情况的状态。
9、redo log buffer是什么?是先修改内存,还是先写redo log文件?
redo log buffer就是一块内存,用来先存redo日志的。在执行CRUD的时候,数据的内存被修改了,redo log buffer也写入了日志。但是,真正把日志写到redo log文件(文件名是 ib_logfile+数字),是在执行commit语句的时候做的。
10、当MySQL去更新一行,但是要修改的值跟原来的值是相同的,这时候MySQL是去执行一次修改还是看到值相同就直接返回呢?
InnoDB认真执行修改这个操作,该加锁的加锁,该更新的更新。