迭代器

一致的观点

如果 ReadOptions.snapshot 放弃,迭代器将返回快照时的数据。如果是nullptr,迭代器将在创建迭代器时从隐式快照中读取数据。 隐式快照过固定资源来保存 ( https://github.com/facebook/rocksdb/wiki/Iterator#resource-pinned-by-iterators-and-iterator-refreshing )。无法将此隐式快照转换为显式快照。

错误处理

Iterator::status() 返回迭代的错误。错误包括I/O错误、校验和不匹配、不支持的操作、内部错误或其他错误。

如果没有错误,状态是status::OK()。如果状态不确定,迭代器也将无效。换句话说,如果Iterator::Valid()为真,则status()被保证为OK(),因此无需检查status()就可以安全地进行其他操作:

  1. for (it->Seek("hello"); it->Valid(); it->Next()) {
  2. // Do something with it->key() and it->value().
  3. }
  4. if (!it->status().ok()) {
  5. // Handle error. it->status().ToString() contains error message.
  6. }

另一方面,如果Iterator::Valid()为false,则有两种可能性: (1)到达数据的末尾。在本例中,status()是OK();有一个错误。在这种情况下,status()不是OK()。 如果迭代器无效,检查status()总是一个好的实践。

Seek()和SeekForPrev()丢弃以前的状态。

注意,在5.13版本中。x或更早(在2018年5月17日合并的https://github.com/facebook/rocksdb/pull/3810之前)status()和Valid()的行为曾经不同:

  • 即使status()不正确,Valid()也可以返回true。这有时可以用来跳过损坏的数据。不再支持此功能。处理损坏数据的预期方法是RepairDB()(参见db.h)
  • Seek() 和 SeekForPrev()并不总是丢弃以前的状态。Next()和Prev()并不总是保持非ok状态。

迭代上绑定

可以通过设置ReadOptions指定范围查询的上限。传递给NewIterator()的读选项的iterate_upper_bound。 通过设置这个选项,RocksDB不必在上界之后找到下一个键。在某些情况下,可以避免一些I/Os或计算。 在某些特定的工作负载中,改进可能非常显著。注意,它既适用于向前迭代,也适用于向后迭代。

有关更多信息,请参见选项的注释。

由迭代器和迭代器刷新固定的资源

迭代器本身不会使用太多内存,但它可以防止释放某些资源。这包括:

  1. memtables和SST文件的创建时间。即使在刷新或压缩之后删除了一些memtables和SST文件,如果迭代器固定了它们,仍然会保留它们。
  2. 当前迭代位置的数据块。如果没有设置块缓存,这些块将保存在内存中,或者固定在块缓存中,或者保存在堆中。 请注意,虽然通常块很小,但在某些极端情况下,如果值非常大,一个块可能非常大。

因此,迭代器的最佳用法是保持它的生命周期,以便及时释放这些资源。

迭代器有一些创建成本。在某些用例中(特别是仅内存用例),人们希望通过重用迭代器来避免迭代器的创建成本。 当您这样做时,请注意,如果迭代器过时,它可能会阻止资源的释放。因此,如果过了一段时间后它们没有被使用,请确保销毁或刷新它们,例如一秒钟。 当您需要处理这个陈旧的迭代器时,在5.7版本之前,您需要销毁迭代器并在需要时重新创建它。从5.7版本开始,您可以调用API迭代器::Refresh()来刷新它。 通过调用这个函数,迭代器将被刷新,以表示最近的状态,并释放之前固定的陈旧资源。

前缀迭代

前缀迭代器允许用户在迭代器中使用bloom filter或hash索引,以提高性能。 但是,该特性有局限性,如果使用不当,可能返回错误的结果而不报告错误。 因此,我们建议您谨慎使用此功能。有关如何使用该特性,请参见Prefix Seek。( https://github.com/facebook/rocksdb/wiki/Prefix-Seek-API-Changes ) 选项total_order_seek和prefix_same_as_start只适用于前缀迭代。

预读

RocksDB会自动提前读取数据,并在迭代过程中,当注意到同一表文件有超过2个IOs时,就会预取数据。 readahead大小从8KB开始,并且在每个额外的连续IO上呈指数级增长,最高可达256kb。这有助于减少完成范围扫描所需的IOs数量。 此自动预读仅在ReadOptions时启用。readahead_size = 0(默认值)。在Linux上,readahead syscall用于缓冲IO模式,AlignedBuffer用于直接IO模式来存储预取的数据。 (自动迭代器预读从5.12开始用于缓冲IO, 5.15开始用于直接IO)。

如果您的整个用例由迭代控制,并且依赖于OS页面缓存(i。e使用缓冲IO),您可以通过设置DBOptions手动打开readahead。 advise_random_on_open = false。如果您运行在硬盘驱动器或远程存储上,这将更有帮助,但对于直接连接的SSD设备可能没有太多实际效果。

ReadOptions。readahead_size在RocksDB中为非常有限的用例提供了预读支持。该特性的限制是,如果打开,迭代器的常量成本将会高得多。 因此,您应该只在迭代非常大范围的数据时使用它,而不能使用其他方法来处理它。一个典型的用例是,存储是具有非常长的延迟的远程存储,OS页面缓存不可用,并且将扫描大量数据。 通过启用此功能,每次读取SST文件都会根据此设置提前读取数据。注意,一个迭代器可以在每个级别打开每个文件,同时打开所有L0文件。你需要为它们预算你的预读内存。预读缓冲区使用的内存无法自动跟踪。

我们正在寻找改进的阅读提前在RocksDB。