《丁奇-MySQL45讲-02》之归纳总结

redo log(重做日志):采用了WAL(Write-Ahead Logging),即先写日志,在写磁盘,这里的描述很简洁,但却很容易理解错误,重做日志也有缓冲池(redo log buffer),所以这里的描述应该是先将修改的内容写到重做日志的缓冲池中,在更新内存中的数据页,最终会根据CheckPoint机制将脏页刷新到磁盘中,这里的脏页个人认为既包括重做日志,还包括数据页,否则是不是就会出现重做日志的缓冲池和数据页的缓冲池两者写到磁盘的时机问题,那何不一起写入呢,至少它们的脏页都是一致的。有人会好奇CheckPoint机制是啥(书中描述地很复杂),可以分成Sharp CheckPoint和Fuzzy CheckPoint,无非就是决定什么时机刷脏,比如说每10秒或者事务提交或者说缓冲池的大小不够用了,接下来要考虑的是刷多少,全部还是按照一定的百分比,异步还是同步。redo log是有固定大小的,也就是说当写满后就会重新开始覆盖写,简单来说就是循环写,丁奇老师在描述write pos和CheckPoint的关系时没说清楚,write pos表示记录到哪里,随着不断的写入,write pos会越往后(顺时针方向)移,checkpoint点表示刷盘到哪里,也就是checkpoint之前的记录都已经刷新到磁盘中了(逆时针方向),还可以进一步说明checkpoint之前的数据可以被覆盖了,就是没用了,随着不断地刷盘checkpoint会越往后(顺时针方向)移,所以在一开始的时候write pos、checkpoint应该是在相同的位置上,那么接着开始写,write pos开始移到,而checkpoint会因为上面提到的机制也发生移动,为了方便理解丁奇老师说的当write pos超过checkpoint时就会强制执行CheckPoint机制,这里我们假设checkpoint不会移动,随着write pos一直写,checkpoint仍然不移动,直到整个缓冲池都写满了,这个时候write pos和checkpoint又回到了相同的位置上了,如果write pos又要继续移动呢,可是不行啊,因为之前写的内容都还没有刷盘,所以在这种情况下checkponit不得不移动了,也就是丁奇老师说的checkpoint要走在write pos的前面了,总之,redo log是用来做做崩溃恢复的。如图所示:

《丁奇-MySQL45讲-02》之归纳总结

bin log(二进制日志):丁奇老师说bin log不适合做崩溃恢复,一方面是因为它没有checkpoint,无法确切地知道哪些数据页是脏的,谈何恢复。由于它是以追加的方式写入,也就是说bin log的磁盘文件会越来越大,毕竟它是全量文件,也因此更适合用来做备份。

两种日志的区别:

  • redo log是存储引擎特有的,bin log是在Server层实现的,也就是说所有引擎都可以使用。

  • redo log是物理日志,举个例子,比如你将某个值由1改成2,又将2改成3,那么最终物理日志呈现的只有3(而没有记录3的由来),只不过不是直接将3写入,可能是采用偏移量之类的操作方式(无奈我看不懂),bin log是逻辑日志,很好奇怎么样才叫逻辑日志,搜索引擎也没能搜到相关概念,于是尝试看了一下bin log在不同模式下的内容,虽然能看到一点不同,但仍然不能解决心中的疑问,所以在这里先认为逻辑日志表示记录从1-2-3这样子的一个过程。

  • redo log是循环写的,大小是固定的,bin log是追加的方式写的,不会覆盖以前的日志。

一条更新语句的执行流程(ID是索引),update T set c=c+1 where ID=2:

  • 执行器将携带ID=2的参数调用存储引擎的接口,存储引擎查看数据页的缓冲池中有没有该数据页,如果有的话就直接给执行器(这其中有可能涉及到MVVC),如果没有的话那只能从磁盘读取,并加载到内存中,最后返回给执行器。

  • 执行器拿到数据页后将其做修改,调用存储引擎写入新的数据页。

  • 第三个步骤我认为应该是先写redo log 缓冲池,然后在更新数据页的缓冲池,来假设一种情况,如果是先写数据页缓冲池,刚好写完之后出发了刷脏,那么这些修改就会造成落盘,而在写redo log的时候因为崩溃就失败了,说明这条修改并没有提交成功,那么在恢复的过程是否会拥有之前落盘的数据,redo log并没有这条记录的信息,也谈不上要进行回滚,其实最关键的一点就是看数据页和redo log之间的逻辑,好了,回到正题。写完数据页之后,redo log就处于prepare状态下,然后告知执行器执行完成了,随时可以提交事务了。

  • 执行器收到后就会生成该操作的bin log,并把bin log写入磁盘。

  • 接着执行器调用存储引擎的提交事务接口,存储引擎把之前的redo log改成提交状态,至此更新过程就结束了。

数据恢复过程(不是崩溃恢复):找到之前最近的一次全量备份,将其恢复到临时库上,然后从备份的时间点开始,将bin log依次取出来,重放到某个时间点,比如误删操作之前的时间点,接着就是把临时库的数据取出来,按需恢复到线上库。

证明两阶段提交的意义:如果不使用两阶段提交,不管是先写redo log还是先写bin log都会出现两者的状态不一致。比如先写redo log,然后崩溃了导致bin log没有写,那么在崩溃恢复的时候还可以正常恢复,但对于备份来说,就会少了记录,对于以后的数据恢复来说,都会造成一定的问题,同理先写bin log也是如此。所以两阶段提交是为了保证redo log与bin log的状态一致。

崩溃恢复的过程:先看下redo log处于什么状态,如果是prepare下,那么在查看对应的bin log事务是否存在,两者是根据事务ID进行匹配,对于一个完整的事务,bin log会有一个固定的结尾,如果没有对应的事务,那么就要进行回滚(涉及到undo log),如果有对应的事务的话,那么就会调用存储引擎的提交事务接口;如果redo log处于commit状态,说明bin log写入成功,直接恢复数据。

建议:innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务提交成功,redo log 都直接持久化到磁盘,这样可以保证异常重启之后数据不丢失。sync_binlog 这个参数设置成 1 的时候,表示每次事务成功后, bin log 都持久化到磁盘,这样可以保证异常重启之后 bin log 不丢失。

一天一备跟一周一备的区别:

  • 一天一备在最坏情况下只需要一天的bin log,而一周一备在最坏情况下需要一周的bin log。

  • 一天一备的恢复时间(RTO)自然会比一周一备的时间短。

  • 每天都备份自然会消耗更多的存储空间。

上一篇:MySQL事务


下一篇:binlog 、unlog、relog三者的区别