# 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 都使用在哪些场景