删除过期文件

在这个wiki中,我们解释了如果不需要文件,如何删除它们

SST 文件

压缩完成后,输入的SST文件将被LSM-Tree中的输出文件替换。但是,它们可能没有资格立即被删除。正在进行的操作依赖于较老版本的LSM-Tree,这将防止那些文件被删除,直到这些操作完成。 查看我们如何跟踪实时SST文件,了解LSM-Tree的版本控制是如何工作的。

可以保存较老版本的LSM-Tree的操作包括:

1.迭代器。迭代器在创建时锁定LSM-Tree的版本。禁止删除此版本中的所有SST文件。这是因为迭代器从虚拟快照中读取数据,所以迭代器保存所有SST文件,以便虚拟快照中的数据可用。 2.持续的压缩。即使其他压缩没有压缩这些SST文件,LSM-Tree的整个版本也是固定的。 3.Get()期间的短窗口。在执行Get()的短时间内,固定了LSM-tree版本,以确保它可以读取所有不可变的SST文件。当没有操作固定包含SST文件的旧LSM-tree版本时,该文件符合删除条件。

通过两种机制删除符合条件的文件:

引用计数

RocksDB在内存中保存每个SST文件的引用计数。LSM-Tree的每个版本都包含该版本中所有SST文件的一个引用计数。依赖于此版本的操作(如上所述)反过来通过”超级版本”直接或间接地保存对LSM-Tree版本的引用计数。 一旦一个版本的引用计数降到0,它就会删除其中所有SST文件的引用计数。如果文件的SST引用计数下降到0,则可以删除它。通常会立即删除,但以下情况除外:

1.在关闭迭代器时发现不需要该文件。如果用户设置了ReadOptions.background_purge_on_iterator_cleanup=true,我们没有立即删除文件,而是将一个后台作业调度到high-pri高级线程池(删除刷新作业的同一池)来删除文件。 2.在Get()或其他一些操作中,如果它取消了对某个版本的LSM-Tree的引用,并导致一些SST文件过期。保存这些文件而不是删除这些文件。下一个刷新作业将清理它,或者如果其他一些SST文件被另一个线程删除,这些文件将一起删除。 通过这种方式,我们确保在Get()中没有执行文件删除I/O,注意,如果没有刷新发生,旧的文件可能会保留在那里被删除。 3.如果用户调用了DB::DisableFileDeletions(). 所有要删除的文件将被保存。一旦DB::EnableFileDeletions()清除了文件删除限制,它将删除所有等待删除的SST文件。

列出所有文件以查找过期文件

引用计数机制适用于大多数情况。但是,引用计数不是持久的,所以在DB重新启动后会丢失它,所以我们需要另一种机制来垃圾收集文件。 在重新启动数据库时,我们会进行完整的垃圾收集,并根据options.background_purge_on_iterator_cleanup周期性地进行垃圾回收。 后一种情况只是为了安全。

在这种完全垃圾回收模式中,我们列出DB目录中的所有文件,并检查每个文件是否针对所有活动版本的LSM-Tree,并查看该文件是否正在使用。 对于不需要的文件,我们删除它们。但是,并不是DB目录中所有不在活动LSM-Tree中的SST文件都过时了。 不应该删除为正在进行的压缩或刷新而创建的文件。为了防止这种情况发生,我们使用了一个很好的特性,即使用增量号的文件名创建新文件。 在每次刷新或压缩作业运行之前,我们都会记住当时创建的最新SST文件的数量。如果在作业完成之前运行了完整的垃圾收集,那么所有大于该值的SST文件都将被保存。 该条件在任务完成后释放。由于多个刷新和压缩作业可以并行运行,因此所有编号大于当前正在进行的作业所记得的最早编号的SST文件都将被保存。有可能我们有一些假积极性,但他们最终会被清除。

Log Files 日志文件

如果日志文件中的所有数据都已刷新到SST文件,则该日志文件具有删除资格。对于单列家族DBs,确定日志文件可以被删除是非常简单的, 对于多列家族DBs稍微复杂一些,当启用两阶段提交(2PC)时,情况就更复杂了。

具有单列族的DB

日志文件与memtable有1:1的映射。一旦memtable被刷新,相应的日志文件将被删除。这是在刷新作业结束时完成的。

具有多个列族的DB

当有多个列族时,在为任何列族刷新memtable时创建一个新的日志文件。只有当其中所有列族的数据都刷新到SST文件时,才能删除一个日志文件。 RocksDB实现它的方法是为每个列族跟踪仍然包含该列族未刷新数据的最早日志文件。只有当日志文件早于所有列族的最早日志时,才可以删除该日志文件。

两阶段提交

在两阶段提交(2PC)的情况下,一个写操作有两个日志条目:一个准备操作和一个提交操作。只有当提交的数据被刷新到内存时,我们才能释放包含要删除的准备或提交的日志。 memtable和日志文件之间不再有清晰的映射。例如,考虑单个列族DB的这个序列:

  1. --------------
  2. 001.log
  3. Prepare Tx1 Write (K1, V1)
  4. Prepare Tx2 Write (K2, V2)
  5. Commit Tx1
  6. Prepare Tx3 Write (K3, V3)
  7. -------------- <= Memtable Flush <<<< Point A
  8. 002.log
  9. Commit Tx2
  10. Prepare Tx4 Write (K4, V4)
  11. -------------- <= Memtable Flush <<<< Point B
  12. 003.log
  13. Commit Tx3
  14. Prepare Tx5 Write (K5, V5)
  15. -------------- <= Memtable Flush <<<< Point C

在A点,虽然memtable被刷新,但是001.log没有资格被删除,因为Tx2和Tx3还没有提交。 同样,在点B中,0001.log仍然没有资格删除,因为Tx3还没有提交。只有在C点001.log中可以删除。但是002.log仍然不能因为Tx4而被删除。

RocksDB使用复杂的低锁数据结构来确定日志文件是否符合删除条件。