
140
|
第
7
章
所有已经接受的写操作都必须等待。这样,刷写进程(作为消耗者)就知道哪些其他进
程(作为生产者)依赖于它。
更一般地,我们有以下同步点:
memtable
切换
在此之后,所有的写操作都去到新的 memtable,使其成为主 memtable,而旧的
memtable 仍可用于读操作。
刷写完成
在表视图中用刷写完的磁盘驻留表来替换旧的 memtable。
预写日志截断
丢弃持有与被刷写 memtable 相关联的记录的日志段。
这些操作隐含着严苛的正确性要求。继续写入旧的 memtable 可能导致数据丢失(例如,
如果写入已被刷写的 memtable 段)。类似地,在驻留磁盘的对应部分准备就绪之前,如
果旧的 memtable 不提供读取,则将导致读取到不完整的结果。
在压实期间,表视图也会更改,但这里的过程稍微简单一些:旧的磁盘驻留表被丢弃,
而被压实的版本则被添加进来。旧表必须保持可访问性,直到新表完全写入并准备好代
替它进行读操作。同一个表参与多个并行压实的情况也必须避免。
在 B 树中,日志截断必须与从页缓存中刷写脏页相协调,以保证持久性。在 LSM 树
中,我们有一个类似的要求:写入操作在 memtable 中被缓冲,并且它们的内容在完
全刷盘之前是不具备持久性的,因此日志截断必须与 memtable 刷盘相协调。刷盘一
旦完成,日志管理器就会得到最新的已刷写日志段的信息,因此可以安全地丢弃其中
的内容。
不将日志截断与刷盘同步也会导致数据丢失:如果在刷盘完成之前丢弃了日志段,并且在
此时节点崩溃,那么这些日志内容将不会被重放,从而导致该段的数据也不会被恢复。 ...