# FragmentedRangeTombstoneList - 类职责: - 切分 tombstones 为不重叠的 fragments 并持有这些 fragments ## RangeTombstoneStack - [PR: Modify FragmentedRangeTombstoneList member layout #4632](https://github.com/facebook/rocksdb/pull/4632) - 一个 RangeTombstoneStack 包含了一个 tombstone 的 : - **`start_key`**: `Slice`,user key - **`end_key`**:`Slice`,user key - **`seq_start_idx`**, **`seq_end_idx`**:`size_t` - start 和 end 的 seqnum 在 seqnum vector 中的索引 - `[seq_start_idx, seq_end_idx)` 范围内的 seqs 个数就表示了范围相同的 tombstone 的数量 ``` tombstones_: [a, b) [c, e) [h, k) | \ / \ / | | \ / \ / | v v v v tombstone_seqs_: [ 5 3 10 7 2 8 6 ] ``` - `tombstones_`: `vector<RangeTombstoneStack>` - 按范围去重后的 tombstones - `tombstone_seqs_`:`vector<SequenceNumber>` - 个数等于 tombstone 的个数 - 同范围 seqno 由大到小排列 - 上例中对应的 tombstones 为: | `[a, b)@5` | `[a, b)`@3 | `[c, e)@10` | `[c, e)@7` | `[c, e)@2` | `[h, k)@8` | `[h, k)@6` | | ---------- | ---------- | ----------- | ---------- | ---------- | ---------- | ---------- | ## 核心成员 - **`tombstones_`**:`vector<RangeTombstoneStack>`,不重叠的 tombstone fragments - **`tombstone_seqs_`**:`vector<SeqenceNumber>`,tombstone fragment seqs - **`seq_set_`**:`set<SequenceNumber>`, 有序的 `tombstone_seqs_` - lazily initializing - **`num_unfragmented_tombstones_`**: `uint64_t` - 输入的 `unfragmented_tombstones` 个数 - **`total_tombstone_payload_bytes_`**:`uint64_t` - 输入的 `unfragmented_tombstones` start key 和 end key 的大小总和 ## FragmentTombstones() - 入参: - **`unfragmented_tombstones`**:`unique_ptr<InternalIterator>` - 按起始 key 有序的 range tombstone iterator - 迭代器的 key 为 tombstone 的 start ikey,value 为 end ikey。 - **`for_compaction`**:`bool` - **`snapshots`**:`vector<SequenceNumber>` - 将 `unfragmented_tombstones` 切分成不重叠的片段(fragment) - 每个不重叠的片段就是 `tombstones_` 中的一个 RangeTombstoneStack - 如果 `for_compaction` 为 true,则应该提供 `snapshots`. - 在所有快照中都不可见的 fragment 都将被丢弃 - 对于每个 snapshot stripe `[lower, upper]`,只保留在此范围内的 seqno 最大的 tombstone,其他==丢弃==(优化,减少无用搜索) - 例如 snapshot stripe 为 `[3, 10]`,tombstone seqs 为 `{5, 6, 7}`,则 10 可见的只有 7,其他的如 5,6 则丢弃 - [PR: Prepare FragmentedRangeTombstoneIterator for use in compaction #4740](https://github.com/facebook/rocksdb/pull/4740) ## 构造函数 - 入参: - **`unfragmented_tombstones`**:`unique_ptr<InternalIterator>` - 按起始 key 有序的 range tombstone iterator - 迭代器返回 key 为 tombstone 的 start ikey,value 为 end ikey。 - **`for_compaction`**:`bool` - **`snapshots`**:`vector<SequenceNumber>` - 遍历 `unfragmented_tombstones`迭代器,检测是否有序,无序的话进行==排序==,最后调用 `FragmentTombstones()` ## ContainsRange() - 入参: - **`lower`**, **`upper`**:`SequenceNumber` - 检查是否存在 seqno 位于 `[lower, upper]` 之内的 tombstone - 通过二分搜索 `seq_set_` ---- # FragmentedRangeTombstoneIterator - 迭代 **FragmentedRangeTombstoneList**,一次迭代位置对应一个 tombstone fragment - key() 返回 tombstone start ==iternal key== - value() 返回 tombstone end ==user key== - 继承了 InternalIterator。 ## 核心成员 - **`tombstone_start_cmp_`**, **`tombstone_end_cmp_`**: 比较器,按 user key 比较 start 和 end - 实现 RangeTombStack 与 Slice 之间可相互比较 - **`tombstones_`**:`FragmentedRangeTombstoneList *`,不重叠的 tombstones - **`tombstones_cache_ref_`**:`shared_ptr<FragmentedRangeTombstoneListCache>` - [Cache fragmented range tombstone list for mutable memtables #10547](https://github.com/facebook/rocksdb/pull/10547) - 引用 cache 并使用其中的 tombstones,此时 `tombstones_` 指向 cache 中的实例 - **`tombstones_refs_`**:`shared_ptr<FragmentedRangeTombstoneList> ` - 使用外部传入的 `shared_ptr` 中的 tombstones - 此时 `tombstones_`指向 `shared_ptr` 中的实例 - **`upper_bound_`**:`SequenceNumber` - **`lower_bound_`**:`SequenceNumber` - **`pos_`**:`vector<RangeTombstoneStack>::const_iterator`, - 当前迭代的 RangeTombstoneStack 位置 - **`seq_pos_`**:`vector<SequenceNumber>::const_iterator` - 当前迭代的 seq 位置 - (`pos_`, `seq_pos`) 两者的组合代表了当前的位置 - **`current_start_key_`**:`InternalKey` - 当前迭代位置的 start internal key(`tombstones_`里的是 user key,因此需要转换) - 用于 key() 函数的返回 ## Valid() - `pos_ != tombstones_.end()` ## Invalidate() - 将迭代器置为失效 - 设置`pos_`为 `tombstones_.end()` - 设置 `seq_pos_` 为 `tombstones_.seq_end()` ## key() - 返回当前 tomstone 的 start **iternal** key - 设置 `pos_->start_key` 到 `current_start_key_`(位置不动只设置一次) - 返回 `current_start_key_.Encode()`即 start ikey slice ## value() - 返回当前 tombstone 的 end **user** key - 即 `pos_->end_key` ## parse_start_key() - 返回当前 fragment start key 对应的 ParsedInternalKey: - `user_key` 为当前 start key - seq 为当前 fragment 的 seq(`seq_pos_`) - type 为 kTypeRangeDeletion ## parsed_end_key() - 返回当前 fragment end key 对应的 ParsedInternalKey: - `user_key` 为当前 end key - seq 为 ==kMaxSequenceNumber== - type 为 kTypeRangeDeletion ## SetMaxVisibleSeqAndTimestamp() - 更新 `seq_pos_`,跳过前面大于 `upper_bound_` 的 seqs。 - 只有不超过 `upper_bound_` 的才可见。 ## SetRangeDelReadSeqno - 设置 `upper_bound_` ## ScanForwardToVisibleTombstone() - 保证当前 fragement seq 位于 `[lower_bound_, upper_bound_]`内,不满足就往 **forward** 方向移动到下一个 tombstone stack 1. 确保 `seq_pos_` 不小于 `lower_bound_` - 小于的话就切换到下一个 tombstone stack(即 `++pos_`) - 相当于跳过小于 `lower_bound_` 的 seqs 2. 同时也会调用 `SetMaxVisibleSeqAndTimestamp()` 跳过大于 `upper_bound_`的 seqs ## ScanBackwardToVisibleTombstone - 确保当前 fragement seq 位于 `[lower_bound_, upper_bound_]`内 - 类似 `ScanForwardToVisibleTombstone()`,不满足 `[lower_bound_, upper_bound_]`时迭代器就往 **backward** 方向走 - 即 `--pos_` ## SeekToFirst() - 指向第一个 fragment ## SeekToTopFirst() 1. 指向第一个 fragment 2. 调用 `SetMaxVisibleSeqAndTimestamp()` 和 `ScanForwardToVisibleTombstone()` 使 seq 满足位于上下界内。 ## SeekToLast() - 移动到最后一个有效的 fragment - `pos_`移动到 `std::prev(tombstones_->end())` - `seq_pos_`移动到`std::prev(tombstones_->seq_end())` ## SeekToTopLast() - 移动到最后一个有效的 fragment, 并保证 seq 在上下界限内。 1. 先移动到最后一个 tombstone stack,即 `pos_ = std::prev(tombstones_->end())` 2. 调用 `SetMaxVisibleSeqAndTimestamp()` 和 `ScanBackwardToVisibleTombstone()` 使 seq 满足位于上下界内。 ## Seek(target) - 移动到第一个可以覆盖 target 或者在 target 之后的 tombstone - 并确保 seq 位于上下界内。 - 通过 `ScanForwardToVisibleTombstone()` ## SeekForPrev(target) - 定位到最后一个可以覆盖 target 或者在 target 之前的 tombstone - 并确保 seq 位于上下界内。 ## Next/TopNext() - 移动到下一个 fragment。 - TopNext 需要应用上下界限。 ## Prev/TopPrev() - 移动到上一个 fragment ## SplitBySnapshot() - [Prepare FragmentedRangeTombstoneIterator for use in compaction #4740](https://github.com/facebook/rocksdb/pull/4740) - 入参: - **`snapshots`**:`const vector<SequenceNumber>&` - 返回值: - `map<SequenceNumber, FragmentedRangeTombstoneIterator*>` - 将当前迭代器按照 N 个快照序列号拆分为 N + 1 范围。 - 例如 snapshots 为 `{3, 5, 7}`,则对应 4 个 seq 范围: - `[0, 3], [4, 5], [6, 7], [8, MAX]` - 针对每个范围 `[lower, upper]` 1. 检查 `tombstones_` 中是否有某个 fragment 的 seq 在这个范围内 2. 如果在则创建一个新的 FragmentedRangeTombstoneIterator 插入到返回值中 - 新 iterator 复用当前 `tombstones_` 但加上了当前范围的 lower 和 bound 值作为其上下界限 - map key 为 upper ## 总结 - 迭代无重叠的 fragment - 带 Top 的函数保证了结果位于某个 seq 上下界限内。 ---- # TruncatedRangeDelIterator - 给 FragmentedRangeTombstoneIterator 指定一个 `[smallest_ikey, largest_ikey]` 范围,对位于范围之外的 tombstones 进行截断。 - 这两个 key 来自 ==SST 文件的边界 key==。 - 或者来自 compaction atomic unit. 见 `TableCache::NewIterator()` - PR: [Introduce RangeDelAggregatorV2 #4649](https://github.com/facebook/rocksdb/pull/4649) ## 核心成员 - **`iter_`**:`unique_ptr<FragmentedRangeTombstoneIterator>` - **`smallest_ikey_`**:`InternalKey *`,构造时传入 - **`largest_ikey_`**:`InternalKey *`,构造时传入 - **`smallest_`**:`ParsedInternalKey *`,从构造参数 `smallest_ikey_` 转换而来 - **`largest_`**:`ParsedInternalKey *`,从构造参数 `largest_ikey_` 转换而来 - 迭代器内部真正使用的是这俩 key。 ## 构造函数 - 入参: - **`iter`**:`unique_ptr<FragmentedRangeTombstoneIterator>` - **`smallest`**:`const InternalKey *` - **`largest`**:`const InternalKey *` - 计算 `smallest_` : - 从 `smallest`参数 unpack,设置 value type 为 `kTypeMaxValid`([最大 type](https://github.com/facebook/rocksdb/pull/11113/files#r1085828909)) - seqno + type 的组合是按降序排序, `kTypeMaxValid`是同 seq 排序顺序上最小的 - 计算 `largest_`, 即从 `largest` 参数 unpack,并调整 `largest_`为开区间 - 如果 type 为 kTypeRangeDeletion 且 sequence 为 `kMaxSequenceNumber`,不需要调整 - 该条件意味着这个文件的边界 largest key 是被 tombstone 人为扩展后生成的,即原文件不存在 user key 相同的 point keys,因此不必使用该 tombstone 过滤文件。 - 如果 sequence 为 0,不需要调整 - sstable largest key seqno 为 0,意味着这是该 user key 的最后一个 seq(seq 降序排序), 同时该 largest ikey 也不会是某个 tombstone 扩展的(否则就会被调整成上一种情况),即使有跟 largest 相同 user key 的 tombstone 存在,也一定是在该 key 前面,不需要调整也能覆盖那个 tombstone。 - 举例说明:原始范围为 `[1, 10]`,10 不是 tombstone,那使用 `[1, 10)`截断 tombstones 是跟 `[1, 10]` 等价的。 - 其他情况: - 对 sequence 进行减一 - 相当于往后扩展一个,因为后续使用的范围 `largest_`为开区间(即不包括它) - type 改写为 `kTypeMaxValid` - 调整后,tombstones 的合法范围为 `[smallest_, largest_)` ## SplitBySnapshot() ^2f02a2 1. 调用 FragmentedRangeTombstoneIterator 的同名方法 2. 对分裂后的每个迭代器应用 `smallest_ikey_` 和 `largest_ikey_`,转换为 `TruncatedRangeDelIterator`。 --- # ForwardRangeDelIterator - 用于迭代 Forward DB Iterator,检测当前迭代位置的 key 是否被 tombstone 覆盖了。 ## 核心成员 - **`unused_idx_`**:`size_t`,外部(StripeRep)中使用 - 跟踪外部的迭代器数组哪些还没有加入到当前类中 - **`active_seqnums_`**:`ActiveSeqSet` - 实际类型: `multiset<TruncatedRangeDelIterator *, SeqMaxComparator>` - active 迭代器集合,按迭代器当前位置的序列号排序,序列号大的在前面 - active:迭代器当前 tombstone 可以覆盖目标 key - **`active_iters_`**: - `BinaryHeap<ActiveSet::iterator, EndKeyMinComparator>` - active 迭代器集合,按 end ikey 比较的小顶堆,最小的 end ikey 在堆顶。 - **`inactive_iters_`** - `BinaryHeap<TruncatedRangeDelIterator*, StartKeyMinComparator>` - inactive 迭代器集合,按 start ikey 比较的小顶堆,最小的 start ikey 在堆顶。 - 这些迭代器当前 tomestone 的 start key 都超过了目标 key ## PopActiveIter() - 从 `active_seqnums_` 和 `active_iters_` 中删除并返回堆顶元素。 ## PushIter() - 参数: - **`iter`**:`TruncatedRangeDelIterator` - **`parsed`**:`const ParsedInternalKey &` - 如果 iter 非 Valid,直接返回。 - 如果 `iter->start_key()` 大于 parsed,则放入 inactive,否则放入 active - 此前 iter 已经 Seek 到 parsed,不大于则意味着 iter 当前 tombstone 可以覆盖 parsed ## AddNewIter() - 参数: - **`iter`**:`TruncatedRangeDelIterator` - **`parsed`**:`const ParsedInternalKey &` - Seek iter 到 parsed user key,然后调用 `PushIter()` ## ShouldDelete() - 检测目标 key 是否是被删除的(即被 tombstone 覆盖) - 入参:`const ParsedInternalKey& parsed` - 执行流程: 1. 根据 parsed key 更新 active 集合 - 即保证 `active_iters_` 堆顶 end key 超过 parsed(此时整个集合也都超过了) 1. 不满足则取堆顶,调用 Next() 移动迭代器,直至 end key 超出 parsed 2. 移动后,迭代器可能因为 start key 也超过 parsed 而变成 inactive - 调用 PushIter() 自动放入合适的集合 2. 根据 parsed key 更新 inactive 集合 - 即保证 `inactive_iters_` 堆顶 start key 大于 parsed(此时整个集合也都大于了) 1. 不满足则取堆顶,调用 Next() 移动迭代器,直至 end key 超出 parsed 2. 再调用 PushIter() 自动放入合适的集合 3. 检测是否 ShouldDelete - 如果 active 不为空,且头部的 seq 大于 parsed 的,则返回 true - 其他情况返回 false - 前提:每次调用传入的 parsed key 都是==递增==的,不然算法就不 work 了。 ## Invalidate() - 清空所有集合 - 清零 `unused_idx_`,以便重新加入所有迭代器 --- # ReverseRangeDelIterator - 整体上跟 ForwardRangeDelIterator 类似,只是方向相反: - 往 Prev 方向迭代 TruncatedRangeDelIterators - 维护的 active / inactive 排序方式也相反。 --- # RangeDelAggregator - 聚合多个 SST 文件的 tombstones iterators,对外提供搜索能力(ShouldDelete)。 - 核心成员 - **`files_seen_`**:`set<uint64_t>`,确保同一个 SST 的 tombstone iterator 只添加一次。 - 调用AddTombstones() 前,先 insert 它,insert 成功再继续。 - 成员函数大多为 pure virtual。 - AddTombstones() - ShouldDelete() - InvalidateRangeDelMapPositions() - IsEmpty() ## StripRep ### 核心成员 - **`iters_`**:`vector<unique_ptr<TruncatedRangeDelIterator>>` - **`forward_iter_`**:`ForwardRangeDelIteartor` - **`reverse_iter_`**:`ReverseRangeDelIterator` - **`upper_bound_`**:`SequenceNumber` - **`lower_bound_`**:`SequnceNumber`,构建时传入,表示对应的 snapshot stripe 的范围 ### AddTombstones(iter) - push back 到 `iters_` 中。 ## InStripe(seq) - 返回 seq 是否位于 `[lower_bound_, upper_bound_]` 内。 ### ShouldDelete() - 参数: - **`parsed`**: `const ParsedInternalKey &` - **`mode`**:`RangeDelPositioningMode` - 枚举类型,迭代方向: - kForwardTraversal - kBackwardTraversal - 执行流程: - 如果非 InStripe 或者 迭代器为空,直接返回 false。 - 根据 mode 参数,Invalidate/重置相反方向的 RangeDelIteartor - 选择与 mode 相同的 RangeDelIteartor(`forward_iter_` 或者 `reverse_iter_`) - 将 `iters_`中新加入的迭代器添加到 iter 中 - 调用 iter 的 ShouldDelete 方法 ## IsRangeOverlapped() - 参数: - **`start`**, **`end`**:`Slice`,user key 范围 - 检测 `iters_` 中是否有 tombstone 跟目标范围有重叠。 ## 总结 - 保管了一个 snapshot strip 对应的一组 `TruncatedRangeDelIterator`s - 并基于它们,封装了两个 forward 和 reverse 两个方向的 RangeDelIteartor。 --- # ReadRangeDelAggregator - 继承了 RangeDelAggregator,封装了单个 StripeRep,各个方法基本上是直接转发给 StripeReq。 - 核心成员 - **`rep_`**:`StripeRep` - upper bound 构造时提供 - lower bound 为 0 ## AddTombstones() - 参数: - **`input_iter`**:`unique_ptr<FragmentedRangeTombstoneIterator>` - **`smallest`**:`const InternalKey *` - **`largest`**:`const InternalKey *` - 基于入参构造一个 TruncatedRangeDelIterator,并添加到 `rep_` 中 ------- # CompactionRangeDelAggregator - 继承了 RangeDelAggregator,按照 snapshot strips 分割,封装了多个 StripeRep。 ## 核心成员 - **`parent_iters_`**:`vector<TruncatedRangeDelIterator *>` - 保存通过 AddTombstones() 添加的所有 `TruncatedRangeDelIterator`s - **`reps_`**:`map<SequenceNubmer, StripeRep>` - key 为 snapshot stripe 的 upper bound - **`snapshots_`**:`const vector<SequenceNumber> *`,构造时传入 ## AddTombstones() - 参数: - **`input_iter`**:`unique_ptr<FragmentedRangeTombstoneIterator>` - **`smallest`**:`const InternalKey *` - **`largest`**:`const InternalKey *` - 执行流程: 1. 构造 `TuncatedRangeDelIterator` 即 iter,将它 push back 到 `parent_iters_` 中 2. 调用 iter 的 [[delete range#^2f02a2|SplitBySnapshot()]] 方法,生成分裂后的多个 `TuncatedRangeDelIterator`s 即 `split_iters` - `split_iters` 为 map结构 - key 为 stripe 的 seq upper bound, - value 为 TuncatedRangeDelIterator 3. 针对每个 `split_iters` ,按 key (upper ) 查看 `reps_` 是否存在对应的 StripeRep,将 TuncatedRangeDelIterator 加入到 StripeRep 中。 - 如果不存在对应的 StripeRep,则先新建。 ## ShouldDelete(parsed, mode) 1. 通过 `reps_.lower_bound(parsed.seq)` 搜索对应的 StripeReq 2. 调用 StripeReq 的 ShouldDelete() 方法 ## 总结 - 按照 snapshot strip 对添加的 tomstones 进行切割分区,可以按 seq 快速定位 stripe,缩小 ShouldDelete 的搜索范围。 --- # 各个类关系图 ```mermaid classDiagram class RangeDelAggregator { <<interface>> +AddTombstones(iter, smallest, largest) +ShouldDelete(ikey, mode) } RangeDelAggregator <|-- ReadRangeDelAggregator: 实现 RangeDelAggregator <|-- CompactionRangeDelAggregator: 实现 class ReadRangeDelAggregator { -StripeRep rep_ } class CompactionRangeDelAggregator { -map~SequenceNumber, StripeRep~ reps_ -vector~SequenceNumber~* snapshots_ } ReadRangeDelAggregator "1" *-- "1" StripeRep: 包含 CompactionRangeDelAggregator "1" *-- "n" StripeRep: 包含多个 class StripeRep { -vector~TruncatedRangeDelIterator*~ iters_ -ForwardRangeDelIterator forward_iter_ -ReverseRangeDelIterator reverse_iter_ +ShouldDelete(parsed, mode) } StripeRep "1" *-- "1" ForwardRangeDelIterator: 包含 StripeRep "1" *-- "1" ReverseRangeDelIterator: 包含 class ForwardRangeDelIterator { -BinaryHeap active_iters_ -BinaryHeap inactive_iters_ +ShouldDelete(parsed) } class ReverseRangeDelIterator { -BinaryHeap active_iters_ -BinaryHeap inactive_iters_ +ShouldDelete(parsed) } ForwardRangeDelIterator *-- TruncatedRangeDelIterator: 引用多个 ReverseRangeDelIterator *-- TruncatedRangeDelIterator: 引用多个 TruncatedRangeDelIterator "1" *-- "1" FragmentedRangeTombstoneIterator: 包含 class TruncatedRangeDelIterator { -FragmentedRangeTombstoneIterator* iter_ -ParsedInternalKey* smallest_ -ParsedInternalKey* largest_ } class FragmentedRangeTombstoneIterator{ -FragmentedRangeTombstoneList* tombstones_ -SequenceNumber upper_bound_ -SequenceNumber lower_bound_ } ``` ----- # TODO - [x] https://github.com/facebook/rocksdb/wiki/DeleteRange-Implementation#tombstone-fragments - [[rocksdb-deleterange-wiki]] - [ ] 写入流程 - [ ] memtable - [ ] SST,在哪里构建 fragment - [ ] mutable 的 memtable 如何计算/更新 tombstones - [ ] 读流程 - [ ] memtable 何时在读取时进行 fragment - [ ] table reader 在哪里缓存 fragmented tombstones - [ ] [Use only "local" range tombstones during Get](https://github.com/facebook/rocksdb/pull/4449) - 引入 FragmentedRangeTombstoneIterator - [ ] [Cache fragmented range tombstones in BlockBasedTableReader](https://github.com/facebook/rocksdb/pull/4493) - [ ] [Introduce RangeDelAggregatorV2](https://github.com/facebook/rocksdb/pull/4649) - [ ] Discuss comment - [x] [Remove v1 RangeDelAggregator](https://github.com/facebook/rocksdb/pull/4778) - [x] [Prepare FragmentedRangeTombstoneIterator for use in compaction](https://github.com/facebook/rocksdb/pull/4740) - 按 snapshot strip 分裂 - [ ] [Speed up range scans with range tombstones](https://github.com/facebook/rocksdb/pull/4677) - [ ] [Skip swaths of range tombstone covered keys in merging iterator](https://github.com/facebook/rocksdb/pull/10449) - cascading seek optimization - [ ] [Cache fragmented range tombstone list for mutable memtables](https://github.com/facebook/rocksdb/pull/10547) - [ ] compaction / flush 文件边界 key 的确定(与 tombstone 有何关联) - [ ] 对于 ReadRangeDelAggregator,是否也能像 compaction 一样可以 [dropping](https://github.com/facebook/rocksdb/pull/4740) tombstones that cann't be read - [ ] RangeDelAggregator 都使用在哪些场景