实现崩溃一致性
实现崩溃一致性
实现崩溃一致性
Systems: “先挣钱,后还债”
崩溃会导致数据丢失、目录损坏,甚至文件系统无法挂载
这时候当然是送去修电脑 (File System Check) 了!
(这种图我都能找到???论文件系统加密的重要性)
实现崩溃一致性
File System Checking (FSCK)
根据磁盘上已有的
G
(
V
,
E
)
G(V, E)
G
(
V
,
E
)
,恢复出 “最可能” 的数据结构
是的,这是个算法题
而且一不小心,fsck 时 crash 能 “恢复” 出一个无法挂载、无法修复的文件系统 (
paper
)
实现崩溃一致性
吸取历史的经验和教训
还记得并发的故事吗
1960s,大家争相在共享内存上实现互斥
Relaxed memory model
软件不够,硬件来凑
软硬协同实现崩溃互斥/同步
存储器是同样的 “乱序执行” 模型
不要实现 ad-hoc 的崩溃一致性
存储系统应该也提供一条
fence
指令
软硬协同实现崩溃一致性
实现崩溃一致性
实现崩溃一致性
磁盘:提供的接口
bwrite
收到立即返回,异步写入 (eventually persistent)
bread
如果在缓存里,立即返回数据
如果不在缓存中,调度实际读操作,数据到达后再返回
bflush
等待所有已写入的数据落盘
现代存储系统可以提供更细粒度的 flush
SCSI: Synchronize Cache
SSD: 固件控制
实现崩溃一致性
重新理解 “数据结构”
视角 1: 存储实际
数据结构
(链表、二叉树、……)
文件系统的 “直观” 表示
Multi-write (crash unsafe)
视角 2: Append-only 记录所有
历史操作
“重做” 所有操作得到数据结构的当前状态
容易实现崩溃一致性
1 + 1 > 2
Append 更容易高效实现
实现崩溃一致性
Append-only + Lazy Update
Store buffer
Store 写入 CPU 本地缓存,慢慢传递给其他处理器
LSM (Log-structured Merge Trees)
实现崩溃一致性
用 Atomic Append 实现 Atomic Perform
实现 atomic_perform(operations)
bread 定位到 journal 的末尾
bwrite TXBegin ; operations
bflush 等待数据落盘
bwrite TXEnd (Commit; 实现 Atomic Append)
bflush 等待数据落盘
replay(operations) —— 所以也叫 “redo log”
replay 数据落盘后可以删除 (标记) 日志
实现崩溃一致性
Journaling: 优化
现在磁盘需要写入双份的数据
批处理 (xv6; jbd)
多次系统调用的 Tx 合并成一个,减少 log 的大小
jbd: 定期 write back
Checksum (ext4)
不再标记 TxBegin/TxEnd
直接标记 Tx 的长度和 checksum
Metadata journaling (ext4 default)
数据占磁盘写入的绝大部分
只对 inode 和 bitmap 做 journaling 可以提高性能
保证文件系统的目录结构是一致的;但数据可能丢失
实现崩溃一致性
write() 返回了,其实文件还没写到磁盘!
sync() 系列系统调用
sync: 同步所有文件系统中的所有数据
最强,等于
performance bug
syncfs(fd): 同步 fd 对应的文件系统
fsync(fd): 同步文件 data + metadata
fdatasync(fd): 同步文件 data
最弱,但依旧可以控制 data loss (追加写可能丢失)
API 设计者的难题:要不要再加一个 fddatasync() 呢?
sync (1): 系统调用的封装
-d data only (fdatasync), -f file system (syncfs)
实现崩溃一致性
Journaling 的代价
从应用视角,文件系统的行为可能很 “怪异”
各类系统软件 (git, sqlite, gdbm, ...) 不幸中招
All file systems are not created equal: On the complexity of crafting crash-consistent applications
(OSDI'14)
(工作目录里的小秘密)
更多的应用程序可能发生 data loss
我们的工作: GNU coreutils, gmake, gzip, ... 也有问题
Crash consistency validation made easy
(FSE'16)
更为一劳永逸的方案:
TxOS
xbegin/xend/xabort 系统调用实现跨 syscall 的 “all-or-nothing”
Android 干脆让应用直接用 SQLite 得了