OptFS@SOSP'13
很经典的一篇文件系统工作,囊括了文件系统中最精华的部分: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