>

英文原文:
Crash Consistency - FSCK and Journaling

1. 简介

本文讲述两种方法来增强文件系统的健壮性,也就是说机器的突然故障对数据造成的影响可以被恢复。第一种被称之为FSCK(File System Checker),说白了就是扫描整个磁盘按照各种情况进行恢复,本文对它不感兴趣(因为复杂且不实用,喜欢的可以看后面的参考资料);第二种是Journaling方法,这个是重点。

2. The Crash Consistency Problem

为什么这个问题会存在呢?如果说所有的磁盘写操作都是原子操作,那岂不是就没这个问题了么?最多给Cache也换成那种断点数据不丢失的存储介质。但是事实上,做不到原子啊,天知道在传输的哪一步出问题,而且也不能指望出问题后这个失败的操作没有后遗症。

3. FSCK

不准备详细介绍这个方法,一句话总结:“丢了一把钥匙,我却要翻遍整个屋子”。

4. Journaling

其实这个又被称作Write-Ahead Logging,跟之前说到的Log-Structure的思想差不多,也就是在更新文件状态前先写数据,这两者有着严格的时间先后顺序。简单来说就是先把数据写入磁盘中的Journaling中,确保写入完成之后再将数据写入到它们最终的地址同时更新状态表明写入成功。

首先看看这个Journaling跟文件系统是怎么共存在磁盘的。如下图:

好了,下面正式介绍这个操作吧。

data journaling
假设我们现在要往磁盘里面写入inode,bitmap以及一个datablock,那么为了确保安全,我们先将这些数据写入磁盘的Journaling中,同时我们存入这个操作的元数据,结果是这样子的:

其中的TxB表示translation begin,里面存储了操作的元数据,比如inode、bitmap、datanode的真实目的地址(就是最终要把这些数据存在哪儿)以及某种形式的Transection ID(TID);TxE表明transection end,里面当然也包含了TID了。 那么整个的写入操作的过程是这样的:

Journal write:将上面提到的那个结果TxB和TxW包装的数据写入磁盘的Journaling区域,并且等候操作的完成;
Checkpoint: 将数据写入到它们最终目的地。
一个问题
上面这么操作可行么?答案是不可以,因为真实写操作的时候,磁盘内部可能因为调度而将写的顺序改变,也就是说并不一定是先写TxB,然后Inode 等等,所以可能出现磁盘除了data block外的结果块写完了,然后再去写data block而在这个过程中挂了。这样一来Journaling就认为希望完成了,而实际上却没有。
为了解决这个问题,只能分步写了,见下图:

Journal write: 写传输内容(包括TxB,metadata以及data),等候完成;
Journal commit:写TxE,等候完成;
Checkpoint:同上
Journaling空间管理
按照上面的操作,Journaling空间迟早被耗尽,所以怎么办,必须有空间回收机制。所以呢,就把整个Journaling当做一个环形存储,空间到头了就从头开始写。为了便于管理,新增一个Journal Super Block,见下图:

那么什么时候回收呢?很简单,每次checkpoint之后那些数据就没用了,所以就checkpoint之后回收吧。 以上这种叫做data journaling。

data journaling总体执行顺序

效率? 按照上面的操作应该是可以执行了,但是效率是个问题,因为每个写操作都需要写两次,第一次写入journaling,然后再写到最终目的地。为了提高效率,就考虑直接将data写入到最终目的地。那么问题来了,什么时候写data。必须在Journal commit之前!! 这种叫做metadata journaling。

data write:
Journal metadata write
Journal commit
checkpoint metadata
free
metadata journaling总体执行顺序

一个很玄乎的错误 这个错误是这样的,对于metadata journaling适用。磁盘地址1000出存储了文件夹foo的数据;此时,在这个文件夹下创建一个新文件,于是需要将文件夹foo的数据(这是metadata哦)做修改;然后,删除文件夹foo以及其包含的所有文件;然后写文件foobar,目的地址恰好也是在block1那边,于是一切正常;注意第一步第三步操作都没有被checkpoint,而第二步压根不需要Journaling。此时机器故障了,那么重启之后根据Journaling数据恢复,此时的Journaling是这样的:
恢复的时候首先checkpoint第一步操作,于是将“修改后的文件夹数据”写入地位为1000的block;接着checkpoint文件foobar,由于是metadata journaling,所以foobar的数据本身是没有的,只有metadata。于是乎,foobar的数据永远丢失了~
那么怎么处理这个问题呢?
有两种方法:

只有当被删除的block被从journal中checkpoint之后才去使用;也就是说block被删除之后,如果此时journal中还有关于这个block的操作没有被checkpoint,则不用这个block。
每次删除block后,将Journal中关于这个block的操作设置为revoked,那么恢复的时候,跳过所有的revoked操作。

参考:
http://xubenbenhit.github.io/CrashConsistency.html