如何判断RocksDB中的文件是否过期?

RocksDB底层存储结构是LSM,有很多Level的sst文件,sst文件需要定期compaction。那么,当一些文件参与compaction,生成新文件之后,旧文件会占用一定的存储空间,那么旧文件何时物理退场是一个问题,毕竟这些部分旧文件可能还在被使用。比较粗暴的方法就是这些文件保留特定时间之后,再进行物理退场。不过这种解决方法也存在一个问题,即若特定时间内积累的这些旧文件过多,会占用大量的存储空间。不过这种解决方案虽然存在问题,但是没有其他太多的副作用,实现起来也比较简单。那么RocksDB是如何管理这些旧文件的呢?

对于RocksDB,底层基于LSM结构,由一系列sst文件组成。在每次compaction之后,新生成的文件会注册到数据库中,而参与过compaction的旧文件也需要物理删除。然而,这些旧文件不能被立即删除,因为旧文件中的部分文件可能因为RocksDB中操作被占用,直到这些文件被占用结束。

sst文件列表存储在version数据结构中。每次compaction之后或者内存数据刷新落盘后,就会产生一个新version。同一时间,只有一个“当前”版本。它表示LSM树中最新的文件。任何新的操作或迭代器在整个读过程或者生命周期中都会使用最新版本对应的文件。而所有被占用的版本对应的文件都要被保留。而所有那些没有在version中的文件就应该被删除了。如下举一个例子:

v1={f1,f2,f3} (当前版本对应的文件列表)

存储在磁盘上的文件:f1, f2, f3
这个时候创建了一个新的迭代器1,会使用v1版本
此时,存储在磁盘上的文件依旧是f1, f2, f3
这个时候,发生一次刷新落盘,新增文件f4,此时会创建一个新version:
v2={f1,f2,f3,f4}(当前版本)
v1={f1,f2,f3}(该版本被迭代器1使用)

此时,存储在磁盘上的文件是f1, f2, f3, f4
又发生一次compaction,将f2,f3,f4合并成f5,形成一个新版本v3:
v3={f1,f5}(当前版本)
v2={f1,f2,f3,f4}
v1={f1,f2,f3}(该版本被迭代器1使用)

此时,存储在磁盘上的文件是f1, f2, f3, f4, f5
然而,此时v2既不是最新版本,也没有被任何操作、迭代器使用,那么这个版本理应被删除,包括f4。而v1依旧不能被删除,因为它仍然被迭代器1使用。此时,版本状态如下:
v3={f1,f5}(当前版本)
v1={f1, f2, f3}(该版本被迭代器1使用)
此时,存储在磁盘上的文件为:f1, f2, f3, f5
当迭代器1销毁后,
v3={f1,f5}(当前版本)
v1={f1,f2,f3}
此时,存储在磁盘上的文件为:f1, f2, f3, f5
现在,v1既不是最新版本,也没有被占用,v1理应被删除,包括文件f2,f3。
此时,有效版本如下:

v3={f1,f5}(当前版本)

此时,存储在磁盘上的文件为:f1, f5

可以通过引用技术实现以上逻辑。每一个sst文件以及每一个版本都有引用技术。
sst引用计数变化:
1.当我们创建一个新版本时,我们会对所有文件增加引用计数。
2.当一个版本没有被占用时,这个版本对应的所有文件引用计数都要减1。
3.当一个文件的引用技术降至0时,该文件就可以被物理删除了。
版本引用计数变化:
1.当创建一个版本时,它是最新的版本,此时该版本引用计数为1
2.当一个版本不再是最新的版本时,此时该版本引用计数减1
3.任何人使用某版本时,该版本引用计数加1,使用结束,该版本引用计数减1
4.当一个版本引用计数为0时,该版本应该被删除

5.当一个版本为最新版本或者被使用时,此时该版本引用计数不为0,因为需要保留

有时,对版本的引用计数会直接维护,比如对文件做compaction时。然而,更多情况下,是通过super version数据结构来间接维护引用计数,super version维护内存表以及版本的引用计数。每次阅读过程只需要增加以及减少一次引用计数,而super version会维护版本的引用技术。这种机制能够避免引用技术被锁住较长时间。

RocksDB将所有版本信息维护在VersionSet数据结构中,同时记录“最新”版本。因为每个column family都有各自的LSM,它们有自己的版本列表,有对应的最新版本。但是在整个数据库中,只有一个VersionSet维护所有column failies的版本信息。

彩蛋:下一篇介绍RocksDB如何从代码级别实现以上逻辑。敬请期待!

如果你觉得这篇文章对你或者你的朋友有帮助,欢迎转发~

你可能感兴趣的:(如何判断RocksDB中的文件是否过期?)