OptFS@SOSP'13
-
- [1. Background: Disk Interface](#1. Background: Disk Interface)
- [2. Motivation: Pessimistic Journaling](#2. Motivation: Pessimistic Journaling)
-
- [2.1 Flow of Pessimistic Journaling](#2.1 Flow of Pessimistic Journaling)
- [2.2 Pessimistic Overheads](#2.2 Pessimistic Overheads)
- [3. Motivation: Probabilistic Crash Consistency](#3. Motivation: Probabilistic Crash Consistency)
-
- [3.1 Quantifying Probability](#3.1 Quantifying Probability)
- [3.2 Factors Affect P i n c P_{inc} Pinc](#3.2 Factors Affect P i n c P_{inc} Pinc)
- [4. Summary](#4. Summary)
- [5. Design: Optimistic Crash Consistency](#5. Design: Optimistic Crash Consistency)
-
- [5.1 ADN](#5.1 ADN)
- [5.2 Optimistic Properties](#5.2 Optimistic Properties)
- [5.3 Optimistic Techniques](#5.3 Optimistic Techniques)
- [5.4 Durability vs. Consistency](#5.4 Durability vs. Consistency)
- [5.5 OptFS](#5.5 OptFS)
- [6. Evaluation](#6. Evaluation)
-
- [6.1 Setups](#6.1 Setups)
- [6.2 Reliability](#6.2 Reliability)
- [6.3 Performance](#6.3 Performance)
- [6.4 Resource Consumption](#6.4 Resource Consumption)
- [6.5 Journal Size](#6.5 Journal Size)
- [6.6 Case Studies of `osync`](#6.6 Case Studies of
osync
)
- [7. Conclusion](#7. Conclusion)
很经典的一篇文件系统工作,囊括了文件系统中最精华的部分:Crash Consistency。很多时候做文件系统其实就是做Crash Consistency,而研究Crash Consistency就是研究如何通过排布Metadata布局(例如日志文件系统)以及I/O顺序(例如日志提交)来最大程度发挥文件系统性能。OptFS主要针对journaling file system进行优化,研究重心在于通过保证I/O顺序实现durability与ordering分离从而对某些不用
fsync
的负载有较大的提升。
1. Background: Disk Interface
由于Disk的性能很慢,Disk通过引入写缓存机制提升I/O性能。但这会复杂化崩溃一致性问题,因为I/O的写入是乱序的。因此,文件系统必须通过强制数据更新顺序来保证能够在崩溃后推理崩溃前的文件系统状态。现有Disk提供两种方法来实现这一目标:
-
Disk提供cache flush指令用于端侧同步强制数据写回(类似NVM中的
clwb
)。ATA系列Disk提供flush cache
指令,SCSI系列提供synchronize cache
指令,这些指令保证Disk cache中所有数据都被刷回。为了保证顺序性,每次写完数据块需要等待flush结束,例如:write(A) + flush + write(B) + flush。 -
Disk提供类似
fence
的指令FUA (Force Unit Access)
,这在flush的基础上保证各数据块持久的先后顺序。例如,write(A) + write(B) + flush + FUA,这保证A比B先持久化在Disk上但可以减少flush等待。但因为FUA实现复杂,文件系统一般不用。
2. Motivation: Pessimistic Journaling
由于任何上述Disk的顺序性方案都需要等待数据刷回完毕,因此现有的journaling file system,如:EXT3,为了保证崩溃一致性,需要付出极大的性能代价。这种Journaling机制是Pessimistic(悲观的),因为这种机制认为FS假设文件系统会在任何时刻崩溃,保证崩溃一致性除了保证顺序,还需要保证持久化。
2.1 Flow of Pessimistic Journaling
文件系统的更新顺序一般是写入数据( D D D),然后写入元数据( M M M),元数据可能有多次I/O,包括对位图的更新,文件inode的更新(包括时间戳、多级索引)等。先写入数据的原因在于避免让元数据指向乱数据。为了保证元数据写入的原子性,journaling file system将元数据先写入journal,然后再更新到原来所在的位置。这种原子的元数据更新被称为transaction。
整个更新流程为:在写入数据 D D D后,对Journal写入元数据 J M J_M JM,在 J M J_M JM完成写入后需要通过原子写入提交块( J C J_C JC)以表明Transaction ( T T T)以持久化。最后,文件系统可以原地更新元数据 M M M。写 M M M崩溃可以通过扫journal重做来保证崩溃一致性。
因此,得到写入顺序 D → J M → J C → M D\rightarrow J_M \rightarrow J_C \rightarrow M D→JM→JC→M。这会带来多次顺序性I/O从而降低性能,现有的优化方案主要有两种:
-
注意到如果 J C J_C JC不提交,那么 D D D和 J M J_M JM都是无效的,所以 D D D和 J M J_M JM之间可以没有顺序性。即
D ∣ J M → J C → M D|J_M \rightarrow J_C \rightarrow M D∣JM→JC→M -
使用checksum可以进一步消除 J M J_M JM和 J C J_C JC的顺序性,因为要么二者都写入成功,要么只要其中之一没写入成功就算transaction T T T无效
D → J M ∣ J C ‾ → M D \rightarrow \overline{J_M|J_C} \rightarrow M D→JM∣JC→M
很自然想到其他优化方案:
- D ∣ J M ∣ J C ‾ → M D|\overline{J_M|J_C} \rightarrow M D∣JM∣JC→M不成立,因为如果 J M ∣ J C J_M|J_C JM∣JC写入成功但是 D D D写入失败就寄;
- D → J M ∣ J C ∣ M ‾ D \rightarrow \overline{J_M|J_C|M} D→JM∣JC∣M不成立,因为可以对 A A A和 B B B使用checksum的条件是, A A A与 B B B都不能原地更新,一旦原地更新checksum就会无效,无法校验正确性;
- D ∣ J M ∣ J C ‾ → M \overline{D|J_M|J_C} \rightarrow M D∣JM∣JC→M成立当 D D D不是原地修改的时候,OptFS其实也用了这种思想;
2.2 Pessimistic Overheads
可以看到,就算使用了优化(flush+checksum),这种悲观的Journal机制(使用flush进行ordering保证)会带来巨大的性能下降。
3. Motivation: Probabilistic Crash Consistency
为了提升文件系统IO性能,避免Disk flush可以很好地实现这一目标。但是,崩溃可能在任何时刻发生,数据的写入顺序一旦错误就会导致不一致性发生。当然,也有很一定可能数据写入顺序是正确的,这样也不会有崩溃一致性问题。因此,这一类方法被称为概率性(Probabilistic)崩溃一致性。
3.1 Quantifying Probability
为了量化概率崩溃一致性,作者提出window of vulnerability (W)
:如果数据块 A A A应该在 B B B之前写入,但是 B B B在 t 1 t_1 t1写入,而 A A A在 t 2 t_2 t2写入,那么 W = t 2 − t 1 W=t_2-t_1 W=t2−t1,这意味着,如果文件系统在 W W W内崩溃,那么就出现不一致,反之,如果 A A A已经完成写入,那么就不会出现不一致。如下图所示,数据块5应该在4之后写入,但是5在 t 1 t_1 t1写入,4在 t 2 t_2 t2写入,任何在 W W W内发生的崩溃都会导致不一致,而 t 2 t_2 t2之后则不会不一致。
由此,不一致的概率为所有 W W W的和除以负载总时间:
P i n c = ⋃ W i t w o r k l o a d P_{inc}=\frac{\bigcup W_i}{t_{workload}} Pinc=tworkload⋃Wi
本文通过分析各workload的block trace
,并使用Seagate Cheetah 15k.5 disk mode进行块提交顺序模拟,从而估算 P i n c P_{inc} Pinc。
3.2 Factors Affect P i n c P_{inc} Pinc
影响 P i n c P_{inc} Pinc的因素有很多,包括但不限于:
-
负载
如下图所示,结论符合常理:读负载不容易发生
inconsistency
;随机写负载容易发生disk schedule,所以block顺序会变动,导致较高的不一致概率;顺序写负载不容易发生disk schedule,block的顺序基本不变,所以inconsistency概率极低。 -
队列深度
如下图所示,队列深度越深,disk就越有可能schedule一个最优的I/O顺序以提升I/O性能(虚线),但增大不一致概率(实线)
-
Journal距离文件系统Metadata的位置
主要针对 J C → M J_C \rightarrow M JC→M的顺序。如下图所示,Journal与metadata距离越远,不一致概率越低,这是因为距离越远,disk越不容易交换 J C → M J_C \rightarrow M JC→M的顺序
4. Summary
-
总而言之,Pessimistic Journaling或是Probabilistic Journaling要么性能极差,要么不能保证崩溃一致性。
-
Pessimistic Journaling慢的原因是一定要持久化,而Probabilistic Journaling不能保证崩溃一致性的本质在于Disk会对数据块重排。
-
因此,本文提出Optimistic Crash Consistency,在Probabilistic Journaling的基础上更进一步,通过保证持久化的顺序性来保证崩溃一致性。其收益在于将Ordering与Durability分开了,需要Durability的时候才进行Disk flush。
5. Design: Optimistic Crash Consistency
实现Optimistic Crash Consistency的核心要义有二:
- 其一,Journal写顺序性保证。Disk Interface没有提供顺序性接口,因此Optimistic Crash Consistency试图在软件层面尽可能消除顺序性;这可以通过checksum做到,即 D ∣ J M ∣ J C ‾ → M \overline{D|J_M|J_C} \rightarrow M D∣JM∣JC→M。值得说明的是,写入的持久化当 D ∣ J M ∣ J C ‾ \overline{D|J_M|J_C} D∣JM∣JC写入时就已经完成,因为可以通过扫描Journal恢复重做 M M M。
- 其二, M M M写顺序性保证。通过为Disk Interface添加一个Asynchronous Durability Notification(异步持久化通知机制,ADN)来告知文件系统Transaction已经完成持久化,以保证 M M M写入顺序性。
这里值得一提的是,不做Checksum似乎也能够通过ADN来实现 D D D, J M J_M JM, J C J_C JC的顺序持久化,这里主要是为了fsync()
性能考虑,消除Journal顺序性可以保证高效的fsync
。
5.1 ADN
ADN的实现目前基于Disk的Parameters进行设计,其本质在于默认一个数据块最大不超过时间 t t t完成写入,那么时间 t t t后就默认数据块已经持久化。本文ADN被设置为30s。
5.2 Optimistic Properties
Optimistic Crash Consistency就是要保证提交的顺序性,以使文件系统能够推断出任何崩溃的点,并进行恢复。下图举了4个例子,其中 J M : i J_M:i JM:i代表Transaction T i T_i Ti里的 J M J_M JM; J C : i ( c k ) J_C:i (ck) JC:i(ck)代表提交块,内含 J M J_M JM和 D D D的Checksum。
- T 0 T_0 T0: D ∣ J M ∣ J C ‾ \overline{D|J_M|J_C} D∣JM∣JC完成持久化,但是 M M M还没有写。恢复时根据 D ∣ J M ∣ J C ‾ \overline{D|J_M|J_C} D∣JM∣JC重做 M M M。
- T 1 T_1 T1: D D D未持久化 J M ∣ J C ‾ \overline{J_M|J_C} JM∣JC持久化,此时Checksum校验失败( D D D没写完),因此 T 1 T_1 T1视为未提交。
- T 2 T_2 T2: J C J_C JC未持久化,此时Checksum校验失败,因此 T 2 T_2 T2视为未提交。
- T 3 T_3 T3: T 1 T_1 T1( D D D未持久化)和 T 2 T_2 T2( J C J_C JC)都未完成持久化,因此 M : 3 M:3 M:3不能够写入。
5.3 Optimistic Techniques
为了实现Optimistic Crash Consistency,文章使用了多种技术:
-
In-Order Journal Recovery
恢复的过程中Journal的扫描必须是顺序的,如果 T i T_i Ti无效,那么Transactions T j , j > i T_j, j>i Tj,j>i都无效;
-
In-Order Journal Release
Transaction T i T_i Ti可以被释放直到ADN通知 M : i M:i M:i完成写入。又因为需要保证Transaction间的顺序性,因此Transaction的释放需要按序进行。
这里感觉是ADN背锅,假设 T 1 T_1 T1先后 T 2 T_2 T2持久化了,但 M : 1 M:1 M:1和 M : 2 M:2 M:2的持久化顺序并不同,这导致释放Transaction的先后可能不同。可能作者假设先 T 1 T_1 T1持久化完立刻Checkpoint M : 1 M:1 M:1,由于ADN目前实现的通知间隔是定长,所以 M : 1 M:1 M:1一定在 M : 2 M:2 M:2之前持久化。
-
Checksums
对 D D D和 J M ∣ J C J_M|J_C JM∣JC同时做Checksum以消除Journal写入的顺序性。这里需要注意数据块 D D D不能做原地修改。因此,后面会看到,本文将一些数据块修改也放到了Journal中。但正如博主认为的,有了ADN后,这种方式的必要性存疑。
-
Background Write After Notification
由于 M M M在后台被写入,因此就算
fsync
,ADN也不会造成额外的等待开销( D D D, J M J_M JM, J C J_C JC的顺序性不由ADN保证,而由Checksum保证)。 -
Reuse after Notification
这里主要考虑数据块Reuse。如果文件 A A A释放了一个块 D D D,文件 B B B分配一个新块 D D D写,那么可能 D D D已经被写脏了(Reuse),崩溃后,文件 A A A可能看到部分属于 B B B的数据。为了解决这一问题,需要添加额外的Free Transaction:该Transaction包含文件A释放 D D D的信息;
Free Transaction必须在B写数据块前持久化。传统的方案可以用flush保证,而Optimistic方案也必须等ADN通知,性能开销较大。为了解决这一问题,在进行数据块分配时,优先分配Free Transaction已经持久化的块(被称为durably-free)。
Durably-free块可以通过ADN后台标记,在空间充足的时候很容易找到,但在空间不足时可需要同步等待。
-
Selective Data Journaling
这里主要考虑File Overwrite。Overwrite可以通过类似CoW的方式解决,但是CoW破坏了文件数据的局部性(对Disk来说可能极不友好),因此可以采用把数据块Journal的方式,然后做原地更新。变为 J D ∣ J M ∣ J C ‾ → M ∣ D \overline{J_D|J_M|J_C}\rightarrow M|D JD∣JM∣JC→M∣D。
当然,作者反复强调这个feature是optional的,当且仅当对文件布局有很高的需求时。
5.4 Durability vs. Consistency
现实Application需要Consistency也需要Durability。因此,Optimistic Crash Consistency提供osync
和dsync
两个接口,前者仅保证Ordering(Consistency);后者保证Ordering及持久化(Durability)。
5.5 OptFS
作者在Linux 3.2上实现了OptFS来验证Optimistic Crash Consistency,主要基于EXT4,对JBD2和VM system做了修改。
6. Evaluation
6.1 Setups
- CPU + DRAM: Intel Core i3-2100 CPU with 16 GB DRAM
- Disk: Hitachi DeskStar 7K1000.B 1TB Disk drive
- OS: Linux 3.2
6.2 Reliability
用Block Trace来验证Crash Consistency。主要是通过把Trace的Block顺序打乱,然后生成文件系统镜像并挂载,可以看到全部一致。
6.3 Performance
-
微基准测试
- OptFS Sequential Overwrites不行是因为Data Journal带来的二次写入;
- OptFS Random Write行是因为随机的覆盖写被转换成了Data Journal的顺序写;
- OptFS Create Files行是因为EXT4会有很多后台写,而OptFS仅在Commit的时候写。当取消掉EXT4的后台写后,性能和OptFS差不多。
-
宏基准测试
- OptFS Varmail行是因为OptFS可以用更大的batch写 D D D和 M M M,而EXT4写的粒度比较小;
- OptFS MySQL不行是因为顺序Overwrite导致Data Journal。
6.4 Resource Consumption
-
内存开销更大一点,因为 M M M的写入要等着ADN通知Transaction持久化才行。
-
CPU开销也更大一点,因为要算Data的Checksum。
6.5 Journal Size
Journal越小,就越需要等待 M M M持久化来释放Transaction的空间。性能如下图所示:
6.6 Case Studies of osync
作者通过一个文本编辑器(Gedit)和database(SQLite)来说明OptFS能够为很多应用提供有效的Crash Consistency的同时,实现很好的性能。
- Gedit:文件的原子更新模式 - 创建一个临时文件;fsync该文件;重命名该文件。通过将Gedit的
fsync
改为osync
,然后I/O Reordering来模拟崩溃; - SQLite:SQLite通过临时Log来保证事务ACID性。该Log可以不用保证持久性,作者通过捕捉I/O Trace然后Reordering来模拟崩溃:把一个30KB表的一般的record移动到另一个表中。
7. Conclusion
OptFS的核心Idea或许很简单:将顺序性与持久化分离,此外Soft Update技术也是通过保证元数据提交的顺序性实现类似的Optimistic的目标。但本文最大的优势在于非常强的讲故事能力,对以往Crash Consistency技术的深刻分析、归类,再到Optimistic Crash Consistency水到渠成。
进而,OptFS也深刻反映了研究文件系统就是研究Crash Consistency的特点。无论文件系统如何布局,最终逃离不了Crash Consistency,如何更好的布局/IO模式来实现更高效的Crash Consistency,是文件系统领域历久弥新的话题。