首页 > 解决方案 > 使用RocksDB通过拆分容器来支持key-key-value(RowKey->Containers)

问题描述

支持我有键/值,其中值是字符串的逻辑列表,我可以在其中附加字符串。为了避免将单个字符串项插入队列导致重写整个列表的情况,我将使用多个键值对来表示它。

Key -> metadata of the value such as length and subkey format Key-l1 -> value of item 1 in list Key-l2 -> value of item 2 in list Key-ln -> the lastest value in the list

我将覆盖 RocksDB 中的键比较器,以便对 Key-ln 格式键的排序首先是对键部分进行排序,然后是对 ln 部分进行排序(即按 Key 分组和排序,在同一 Key 值内按 ln 排序)。这样,在初始批量插入和以后的 sst 压缩期间,所有列表项及其根键和元数据都在 sst 中组合在一起。

追加一个新的列表项变成(1)首先读取Key-metadata,得到当前列表大小为n;2) 插入具有新值的 Key-l(n+1)。通过删除 Key-ln 并更新元数据,删除列表项与 RocksDB 一样。为了确保一致性,(1)和(2)将在 RocksDB 事务中完成。

这个设计好像没问题?

现在,如果我想为整个键值(列表)添加 TTL 的另一个功能,我会在 RocksDB 中使用 TTL 支持。我的理解是 TTL 删除过期项目发生在压缩过程中。但是,这种压缩不是在事务下完成的。RocksDB 不知道 Key-metadata 和 Key-ln 条目是相关的。完全有可能存在一个时间窗口,其中 Key->metadata(root node) 被删除而 (Key-ln) 的子节点尚未删除(或相反的顺序)。如果在此时间窗口内,有人读取或更新列表,则会得到不一致的 Key-list。有什么补救办法吗?

谢谢

标签: rocksdb

解决方案


您应该使用Merge Operator,它专为此类附加值用例而设计。您的设计是read-before-write,它具有性能损失,一般情况下应尽可能避免:NoSQL 中的 read-before-write 是什么?.

Options options;
options.merge_operator.reset(new StringAppendOperator(','));
DB::Open(options, kDBPath, &db)
...
db->Merge(WriteOptions(), "key", "value1");
db->Merge(WriteOptions(), "key", "value2");

db_->Get(ReadOptions(), "key", &result); // return "value1,value2"

上面的示例使用了一个预定义的StringAppendOperator,它只是在末尾附加新值。您可以定义自己的 MergeOperator 来自定义合并操作。

在后端,在读取路径上进行合并操作(并进行压缩以减少版本号),详情:Merge Operator Implementation


推荐阅读