在生活中,你一定有过类似这样的经历:
比如部门发礼品、或者说学校发课本。如果在发放的时候,大家一窝蜂的涌了过来,毕竟双拳双敌四手,渐渐你就招架不过来。
为了工作更好做,你会有几个选择,提前打印个名单,一个个来领,领的人在名单上打勾,东西拿走。或者大家都来拿,你看一眼,记在脑海里,但可能中途打个岔就记错了。也可以记住是谁,找个纸记下来,每次记一下或者隔一会记几下。
对比上面的场景,你没有发现,不同的方式,效率上也存在的差别。比如在名单上找到打勾,那就会完全串行,每个人来都得在定位到自己那条信息上,找的过程费时间。
找一张纸,每隔一段时间记一次,这样效率也还挺高,问题就在于别人打岔的频率。
在计算机科学领域也存在这样的时候,比如我们常用的数据库系统里。数据库为了不喝娃哈哈AD钙奶,就能保证ACID中的A和D,使用了一种被称做「WAL」的机制。全称是write-ahead logging。数据库中所有的变更,会先写到日志里,最后才会写到持久存储的数据文件中。像MySQL 里的 redo log 和undo log 就是这种机制。
像你在纸上记录一样,一直不停的向后写,顺序写,速度就会快,时不时的回过头去检查一下,改一下速度就降下来了。
如果大量记录到白纸上的内容,没有及时的汇总记录到一个表格上,那等最后全部汇总也比较费力气。就像数据库里一直写WAL之后就应用到内存数据修改,速度很快,但如果出现故障的时候,就需要重新回放大量的redo log,恢复时间也无法接受。
就像行政急着要结果的时候,你才开始「回放」白纸上的内容,就会慢很多。如果是在发放的过程中,可以过一段时间汇总一下,然后在白纸上加个「标记」,用于一会提示自己上次算到什么位置了。这种就是数据库里的 checkpoint,下次恢复的时候,就直接从checkpoint 开始向后恢复就行,前面的已经持久化到了磁盘,不用再费事了。
此外,计算机里,许多时候,都是一个根据自己的场景权衡的过程。比如对于使用WAL的时候,MySQL 提供了不同的配置来支持什么时机,多长时间将 log 应用到数据文件,毕竟log 写到磁盘也还是要花点儿时间的。每次都刷盘,会影响效率,但间隔时间太长,就会在机器故障的时候丢失数据。
MySQL 默认将log 刷到磁盘的时机有三个:
- 提交一个事务的时候
- 固定大小的 log buffer 满了的时候
- 无论 log buffer 是否满,每秒会刷一次
redo log 写磁盘的过程
redo log buffer和page cache都是在内存中,所以写入这两者都比较快,而fsync则需要消耗磁盘IO。Mysql的后台每隔1秒也会自动将redo log buffer中的内容刷到磁盘中去。
借用MySQL 官方博客的几张图来说明下
那有了redo log,就保证了故障时安全了吗?是的。
机器在故障的时候,内存中包含数据的内容,也就是所谓的「脏页」一般就会丢了,怎么样恢复丢失的数据呢?咱们前面看到,redo log 先刷盘,之后真正的数据库变更才刷盘,所以我们丢失的数据已经保存在磁盘中的redo log里了。重放redolog 就可以。但这里有例外的情况是MySQL 里包含一个 innodb_flush_log_at_trx_commit 的配置,默认是1,即严格的D,非1的情况下会丢失redo log buffer和page cache中的数据。
本文转载自微信公众号「 Tomcat那些事儿」,可以通过以下二维码关注。转载本文请联系 Tomcat那些事儿公众号。