许多用户使用 RocksDB 实现了一个队列服务。在这些服务里,每个队列,新加入的内容会携带一个更大的序列号 ID,而删除的时候,会删除最小的序列号 ID。用户通常按序列号递增的顺序读取队列。
Key 编码¶
你可以简单的按照 <queue_id, sequence_id>
编码他,queue_id 是按照固定长度编码的,而 sequence_id 则使用大端编码。
迭代 key 的时候,用户可以创建一个迭代器,找到 <queue_id, target_sequence_id>
,然后从该处开始迭代。
旧数据删除问题¶
由于最旧的数据要被删掉,在每个 queue_id
开始的地方,可能会有很大数量的”墓碑“。结果而言,下面两个查询会变得很昂贵:
- Seek(
<queue_id, 0>
) - 当你在一个
queue_id
的最后一个 seq ID 的时候,尝试调用 Next()
为了解决这个问题,你可以记住每个 queue_id 对应的第一个和最后一个 seq ID,然后不要跨越这个范围进行迭代。
另一个办法是,当你在 queue_id 里面迭代的时候,可以在迭代中设置一个结束的 key,通过让 ReadOptions.iterate_upper_bound
指向 <queue_id, max_int>``<queue_id+1>
。我们鼓励你总是设置这个,不管你是否因为删除而导致了慢查询。
检查一个 queue_id 的新 seqID¶
如果一个用户处理完一个 queue_id
中的最后一个 sequenec_id
,然后拉取新插入的数据,只需要调用 Seek(<queue_id, last_processed_id>
),然后调用 Next(),然后看下一个 key 是否为同一个 queue_id
。确保 ReadOptions.iterate_upper_bound
指向 <queue_id+1>
以避免删除数据导致的慢查询。
如果你希望进一步优化这种使用场景,避免每次都二分搜索整个 LSM 树,考虑使用 TailingIterator
(或者在某些地方考虑 ForwardIterator
)https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h#L1235-L1241
更快回收删除数据的空间¶
队列服务是 CompactOnDeletionCollector
的一个很好的使用用例,他在压缩的时候会优先考虑更多的删除动作。给 这里 定义的工厂类设置 immutableCFOptions::table_properties_collector_factories