# InternalIterator
- InternalIterator 面向 ikey entries:key 带 seq 和 type。
- `InternalIterator` 实例化自 `InternalIteratorBase` 类
- `InternalIteratorBase<Slice>`
- InternalIterator 的实现类:
- MemTableIterator:迭代 memtable
- LevelIterator:迭代某层的 SSTs
- MergingIterator:合并多个迭代器
- CompactionMergingIterator
- TruncatedRangeDelMergingIter
- FragmentedRangeTombstoneIterator
- ClippingIterator
## InternalIteratorBase 方法
- 绝大多数为纯虚函数。
### SetRangeDelReadSeqno()
- 入参:`SequenceNumber read_seqno`
- 让迭代器只处理 seq 小于等于 `read_seqno` 的 range tombstones。
- 对于 tombstone iterators 它们只会返回序列号小于等于 `read_seqno` 的 tombstones
### Valid()
- 返回当前迭代器是否有效
- 有效的定义:迭代器指向一个 KV 对,且 status 是 ok 的
### SeekToFirst()
- 指向输入的第一个 key。
### SeekToLast()
- 指向输入的最后一个 key。
### Seek(target)
- 入参:`const Slice &target`
- 指向等于或超过 target 的第一个 key。
- 所有的 `Seek*`类方法执行前都会清楚之前的 error status。
### SeekForPrev(target)
- 入参:`const Slice &target`
- 指向等于 target 或在 target 之前的第一个 key。
### Next()
- 移动到下一个 entry。如果到末尾了,Valid() 返回 false。
### Prev()
- 移动到之前的 entry。
### key()
- 获取当前 entry 的 key。
- 返回 Slice,在下次对迭代器修改之前==有效==。
### NextAndGetResult(result)
- 调用 Next(), 并获取当前结果。
- 参数:`IterateResult *result`(返回值)
- 返回值:返回是否 Valid()
- IterateResult
- **`key`** : `Slice`
- **`bound_check_result`**:`IterBoundCheck`
- **`value_prepared`**:`bool`,如果为 false,则需要先调用 `PrepareValue()` 再调用 `value()`
### user_key()
- 返回当前 user key。需要先检查 Valid()。
### value()
- 获取当前 value。返回 Slice,在下次对迭代器修改之前==有效==。
- 需要先检查 Valid(),有时候也需要先调用 `PrepareValue()`
### status()
- 返回 err status。没错就返回 Status::OK
### PrepareValue()
- 有些类型的迭代器,Seek()/ Next() 等操作可能只加载了 key 而没有加载 value。
- 目的:避免读取可能不需要的 value。
- 这些迭代器创建时会指定 `allow_unprepared_value_` 为 true。
- 该函数用于加载 value,在 `value()` 之前调用。
- 例外:`IterateResult::value_prepared` 为 true。
- 如果发送错误,返回 false。
### MayBeOutOfLowerBound()
- 这个迭代器可能会返回比 `ReadOptions::iterate_lower_bound` 更小的 keys。
### UpperBoundCheckResult()
- 如果迭代器已经根据`iterate_upper_bound` 检查了当前 key ,返回检查结果。
### SetPinnedItersMgr(mgr)
- 给迭代器设置 PinnedIteratorsManager,大多数迭代器不需要,默认实现为空操作。
### IsKeyPinned()
- 如果返回 true,则意味着 `key()` 返回的 Slice 会一直有效,只要
- 没有调用 `PinnedIteratorsManager::ReleasePinnedData` 且
- 迭代器没有删除。
- IsKeyPinned() 保证始终返回 true, 如果
- 迭代器创建时指定了 `pin_data` 为 true
- `BlockBasedTableOptions::use_delta_encoding` 为 false
### IsValuePinned()
- 如果返回 true,则意味着 `value()` 返回的 Slice 会一直有效,只要
- 没有调用 `PinnedIteratorsManager::ReleasePinnedData` 且
- 迭代器没有删除。
### Get/SetReadaheadState(info)
- 参数:`ReadaheadFileInfo *`
- 当迭代器移向同一 level 另一个文件时,复用之前文件的 readahead state 来设置新文件的。
- 避免 `readahead_size` 从初始值开始(8KB)
- PR: [Reuse internal auto readhead_size at each Level](https://github.com/facebook/rocksdb/pull/9056)
### IsDeleteRangeSentinelKey()
- 默认实现返回 false。LevelIterator 中使用,表示当前迭代位置是否是文件边界 sentinel key。
### SeekForPrevImpl(target)
- 非虚函数,用于实现 SeekForPrev()
- 流程:
- 调用 `Seek(target)`
- 循环调用 Prev() 直到当前key 小于等于 target
----
# MemTableIteraor
- 用于迭代一个 MemTable 的 Internal Iterator。
## 核心成员
- **`iter_`**:`MemTableRep::Iterator *`,指向底层实现类如跳表的迭代器
- **`bloom_`**:`DynamicBloom`,memtable 的前缀 bloom filter
- **`prefix_extractor_`**:`SliceTransform *`
- **`valid_`**:`bool`,实现 Valid() 方法
- **`status_`**:`Status`,实现 status() 方法
- **`arena_mode_`**:`bool`,是否使用了外部传入的 Arena
- 如果为 true,释放 `iter_` 时只析构不调用 Delete
- **`value_pinned_`**:`bool`,默认为 true,除非开启了`inplace_update_support` 选项。
## 构造函数
- 入参:
- **`mem`**:`MemTable`,目标 memtable
- **`arena`**:`Arena *`,外部提供内存分配器,可为空
- **`use_range_del_table`**:`bool`,扫描 delete range table 还是普通数据 table
- 初始化 `iter_`,三种分支创建不同类型的迭代器:
- range delete 迭代器
- 当参数 `use_range_del_table` 为 true,则从 `mem.range_del_table_` 上创建迭代器
- prefix scan
- 当 `prefix_extractor_` 不为空且 `total_order_seek` 和 `auto_prefix_mode` 为 false 时,调用 `mem.table_->GetDynamicPrefixIterator()`
- 对于跳表实现,等用于 `GetIterator()`
- 同时初始化 `bloom_` 为 `mem.bloom_filter`
- 一般数据迭代器
- 即调用 `mem.table_->GetIterator()` 创建
## Seek(k)
1. 如果有 prefix bloom filter,则检查 filter 中 目标 key 是否存在
- 不存在则设置 `valid_` 为 false 并返回(迭代器结束了)
- 这相当于只能扫描到跟目标 key 前缀相同的数据(即 [prefix seek](https://github.com/facebook/rocksdb/wiki/Prefix-Seek))
2. 调用 `iter_->Seek(k)`,设置 `valid_` 为 `iter_->Valid()`
## SeekForPrev(k)
1. 同样先检查 prefix bloom filter(如果有),filter 每命中则置 `valid_` 为 false 返回
2. 调用 `iter_->Seek(k)`
3. 如果非 Valid() 且 status ok 的话,调用 `SeekToLast()`
- 即 memtable 的所有数据都小于 k,此时 prev 还是可能存在的,移动到最后一条数据即可。
4. 重复调用 `Prev()`直到指向 k 或者 k 之前
## SeekToFirst / SeekToLast / Next / Prev
- 调用 `iter_` 的对应方法
## key() / value()
- 底层 `iter_`只会返回 key,只有 `iter_->key()` 方法。
- key 的结构:
| key_len(varint32) | ikey data | value_len(varint32) | value data |
| ----------------- | --------- | ------------------- | ---------- |
- key() 和 value() 方法都调用 `iter_->key()`,然后从中抽取 key 或 value 部分。
## IsKeyPinned()
- 始终为 true。
## 总结
- 实现上基本是直接调用底层 Memtable 实现类(如跳表)的迭代器
- 但需要处理 prefix seek 逻辑
- prefix seek 先检查 bf,bf 没有就置为 invalid
- 也需要解码 key / value
- 底层类 value() 为空,KV 都在 key() 返回值中。
----
# LevelIterator
- 用于迭代某一层 SSTs 的 Internal Iterator。
## 核心成员
- **`table_cache_`**:`TableCache *`,用于创建某个 SST 的迭代器。
- **`flevel_`**:`LevelFilesBrief *`,目标 level 的文件列表。
- **`level_`**:`int`,目标 level
- **`file_index_`**:`size_t`,当前迭代到哪个文件了
- **`file_iter_`**:[[iterator#^52ba69|IteratorWrapper]],当前文件的迭代器
- 一般为 wrapped 之后的 BlockBasedTableIterator
- 只能迭代出 point keys
- **`range_del_agg_`**:`RangeDelAggregator *`,构造时传入,可能为空
- **`range_tombstone_iter_`**:`unique_ptr<TruncatedRangeDelIterator> *`
- [PR: Skip swaths of range tombstone covered keys in merging iterator](https://github.com/facebook/rocksdb/pull/10449)
- 当前 SST 的 tombstone iterator
- 当移动到一个新的 SST 时,会更新为新 SST 的 tombstone iterator
- 指向 MergingIterator 中 `range_tombstone_iters_`的对应元素,相当于 `unique_ptr` 的空间分配在那里,对其赋值就能更新 MergingIterator 中的对应元素。
- 指针关联的操作在 `MergeIteratorBuilder::Finish()` 中
```mermaid
flowchart TB
subgraph LevelIterator
iter["unique_ptr * range_tombstone_iter_"]
end
subgraph MergingIterator
direction TB
subgraph ts["Tombstone Iter Vector"]
direction LR
t0["unique_ptr"]
t1["unique_ptr"]
t2["unique_ptr"]
end
end
iter --> t1
```
- **`current_value_`**:`FileDescriptor`
- [ ] 未使用
- **`prefix_extractor_`**:`shared_ptr<SliceTransform> &`,来自 cf option
- **`prefix_exhausted_`**:`bool`,用于 prefix seek
- 下一个文件的起始key 跟 Seek 的目标 key 前缀不同,表示前缀整体迭代完成,不需要继续
- **`to_return_sentinel_`**:`bool`
- 当迭代当前 SST 内所有 point keys EOF 后,下次 Next/Prev 返回文件的边界 key
- 该 key 可能是由 delete range 扩展的,这种情况它会超过了(大于或小于)文件内的所有 point keys
- 为 true 表示存在这样的key,且当前迭代位置为 sentinel key
- 前提:仅当`range_tombstone_iter_` 不为空时才返回 sentinel key
- **`sentinel_`**: `Slice`,返回的文件边界 key
- 当调用 `to_return_sentinel_` 为 true,`key()` 函数会返回该 key。
- **`read_seq_`**:`SequenceNumber`,读取时可见的最大 seqno
- 来自 `ReadOptions.snapshot`,没有则为 kMaxSequenceNumber
- 用于限制 SST tombstone iterator 对外可见的 upper bound
<br>
- **`compaction_boundaries`**:`vector<AtomicCompactionUnitBoundary>`
> To be propagated to RangeDelAggregator in order to safely truncate range tombstones.
- [PR: Truncate range tombstones by leveraging InternalKeys](https://github.com/facebook/rocksdb/pull/4432)
- compaction 使用,当创建 compaction input iterators 时,由构造函数中传入
- 表示当前 level 每个 SST 的 AtomicCompactionUnitBoundary,当创建 SST iterator 时,会将 boundary 作为 SST TruncatedRangeDelIterator 的截断边界(而不使用 SST 的范围)
- 见 `TableCache::NewIterator()`
## TrySetDeleteRangeSentinel(boundary_key)
- 前提:
- 当前 SST 迭代到末尾 EOF 了。
- 即 `!file_iter_.Valid()` 且 status 是 ok 的。
- 操作:
- 设置 `to_return_sentinel_` 为 true,同时设置 `sentinel_` 为 `boundary_key`
- `boundary_key`来自文件的 smallest 或 largest key,可能不是文件中的某个 point key 而是 tombstone 扩展而来的。
## ClearSentinel()
- 设置 `to_return_sentinel_` 为 false
## NewFileIterator()
- 将 `file_iter_` 指向 `file_index_` 位置的文件。
- 主要是调用 `table_cache_->NewIterator()`初始化 `file_iter`
- 同时也会更新 `range_tombstone_iter_`
## SkipEmptyFileForward()
- 检查当前文件是否已经迭代完成,结束的话就切换到下一个文件。
- 迭代完成的条件(需同时满足):
1. `to_return_sentinel_`为 false
- true 表示有 sentinel 需要迭代,没有结束
2. `file_iter_`的 status 为 ok 、 Valid() 为 false
3. `file_iter_->UpperBoundCheckResult()` 不为 `kOutOfBound`
- 下一个文件的 key 更大,没必要切换了。不切换的结果就是 LevelIterator 的 `Valid()`会返回 false,迭代中止
- 无法切换到下一个文件:
- 满足任一条件:
- 当前文件是本层最后一个
- 迭代位置超过 ReadOption `iterate_upper_bound` (下一个文件的 key 更大了)
- `prefix_exhausted_` 为 true
- 表示本层次都迭代结束了,执行`file_iter_->Set(nullptr)`,并返回。
- Set 为 nullptr 效果就是设置 `file_iter_`的 `valid_` 为 false
- 见 `IteratorWrapperBase::Set()` 函数
- 可以切换到下一个文件时,执行:
- `file_iter_`切换到下一个文件
- 重置迭代器到起始位置:
- 对 `file_iter_` 进行 `SeekToFirst()`,并调用 `TrySetDeleteRangeSentinel`
- 对 tombstone iter 调用 `SeekToFirst()`
## Seek(target)
1. 先检测目标 key 是否在当前文件(`file_index_`)的范围内,不在则需要 reseek file
- reseek:二分搜索当前 level,切换到新文件,并更新 `file_index_` 和 `file_iter_`
2. 调用 `file_iter_->Seek(target)`,seek 当前文件迭代器
3. prefix seek 状态下,如果下一个文件的前缀与 target 不同,则设置 `prefix_exhausted_`,表示前缀已经迭代完成, `SkipEmptyFileForward`不需要尝试下一个文件
- PR:[LevelIterator to avoid gap after prefix bloom filters out a file #5861](https://github.com/facebook/rocksdb/pull/5861)
4. 基于 largest key 尝试设置 DeleteRangeSentinel(Seek 后可能出于 point keys 末尾)
5. 调用 `SkipEmptyFileForward()`尝试切换到下一个文件(有可能当前文件没有 seek 到有效数据)
- SkipEmptyFileForward 中针对下一个文件直接 `SeekToFirst()`就行,因为新文件肯定大于等于 target,也会尝试设置 DeleteRangeSentinel
- `SeekToFirst()`和 sentinel 两者其中一个必然是有效的。
## Next()
- 如果 `to_return_sentinel_` 为 true,则清除(当前位置为 sentinel,Next消耗了它)
- 否则调用 `file_iter_.Next()`,并基于 largest key 调用 `TrySetDeleteRangeSentinel()`
- 调用 `SkipEmptyFileForward()`查看是否有必要切换到下一个文件
## SkipEmptyFileBackward()
- 类似 SkipEmptyFileForward,当前 `file_iter_`非 Valid()时,切换到前一个文件。
- TrySetDeleteRangeSentinel 时使用文件的 smallest key。
- 用于 `Prev()`, `SeekToPrev()`, `SeekToLast()`
## SeekToLast()
- 设置 `file_iter_` 为当前 level 最后一个文件,并调用 `file_iter_->SeekToLast()`
- 并基于 smallest key 调用 TrySetDeleteRangeSentinel(需要 `range_tombstone_iter_` 不为空)
- 调用 `SkipEmptyFileBackward()`,如果 `file_iter_` 为空则切换为前一个文件
- [x] ❓ 什么情况下文件会为空?
- 文件内只有 range tombstones,没有 point keys
- 且 `range_tombstone_iter_`为空,即不读取 sentinels
- SST largest key 对应的 sentinel 不算作 Last
- 与 Prev 的逻辑一致,backfoward 的 sentinel 只考虑 smallest key
## Prev()
- 类似 Next(),当前文件迭代完成后,切换为上一个文件。
## 总结
- 迭代某一个有序 level 的 SST files。
- 特殊:可选地,能迭代出文件边界 key,用于上层使用者感知文件切换。
- 仅当 `range_tombstone_iter_`指针不为空时,由上层按需指定
- 该 key 可能不存在于 point keys 中而是来自 delete range
- forward 方向的 sentinel 只考虑文件的 largest key,backward 方向则只考虑 smallest key
- [x] 切换文件 NewFileIterator 时,如何更新 tombstone iter 的
- NewFileIterator() 中跟`file_iter`一起更新
----
# IteratorWrapper
^52ba69
- 封装了一个 InternalIterator,提供跟 iterator 类似的接口,为底层 iterator 缓存了 valid() 和 key() 的结果。
- `IteratorWrapper` 是 `IteratorWrapperBase<Slice>` 的别名。
## 核心成员
- **`iter_`**:`InternalIteratorBase<TValue> *`,底层 iter,TValue 默认为 Slice。
- **`result_`**:`IterateResult`,缓存的当前 key 等数据,具体包括:
- **`key`**:`Slice`
- **`bound_check_result`**:`IterBoundCheck`,默认 kUnknown
- **`value_prepared`**:`bool`,默认 true,如果为 false 表示调用 value() 前需要先调用 PrepareValue()
- **`valid_`**:`bool`,缓存的当前 valid 状态
## Update()
- 从底层 iter 同步数据到缓存
- 更新 `valid_` 为 `iter_->Valid()`
- 如果 `valid_` 为 true,更新 `result_`
- key 为 `iter_->key()`
- `bound_check_result`为 kUnknown
- `value_prepared` 为 false
## Seek/SeekToFirst/SeekToLast/Prev
- 调用底层 `iter_` 对应方法,然后调用 Update 更新 key 等缓存。
## Next()
- 调用底层 `iter_->NextAndGetReuslt`,结果存储到 `result_`中
## PrepareValue()
- 如果 `result_.value_prepared` 为 true,则表示已经 prepare 过了,直接返回。
- 否则调用 `iter_->PrepareValue()`,更新 `result_`
## Set(iter)
- 设置底层 iter,并返回 `iter_` 旧值。
- 如果入参为 nullptr, 设置 `valid_` 为 false;否则调用 Update() 同步缓存。
## DeleteIter(is_arena_mode)
- 删除迭代器,如果 `is_arena_mode` 为 false 直接 delete,为 true 只调用析构函数。
------
# MergingIterator
- internal iterator,合并多个来自不同 level 迭代器,并应用 range tombstones。
- L0 有重叠,每个文件算一个 level。
## Doc Comment
- MergingIterator 使用了一个 min/max heap 来合并多个 point iterators 的数据。也可以添加 range tombstones, 被 tombstones 覆盖的的 keys 将会被跳过。
### 处理 range tombstones
- 将 tombstone 的 start 和 end 两个 key 跟普通 point keys 一起放入 `minHeap_`或 `maxHeap_`。
- 每个 range tombstone 仅在它的内部 key 范围 `[start_key, end_key)` 内是 active 的。`active_` set 用于跟踪有 active range tombstone 的 levesl。
- 以 forward scan 为例:
- Level j 会位于 `active_` 中当它当前的 range tombstone 满足
- tombstone 的 start key 已经从 `minHeap_`中弹出,且 end key 还在 `minHeap_`中
- 如果`minHeap_`堆顶是来自 Level L 的一个 point key,通过检查 `active_` 中是否有小于等于 L 的 level 存在比如 `l` 就可以判断该 point key 是否被 tombstone 覆盖。
- 当 `l == L` 时,还需要进一步检查 tombstone 的 seqno。
### Invariants
- 在 forward scan期间,MergingIterator 会维持以下的 invariants:
- 在调用每个 InternalIterator API 后,如 `Seek*()`,`Next()` 和 `FindNextVisibleKey()`,如果 `minHeap_`非空,则:
1. `minHeap_.top().type == ITERATOR`
2. `minHeap_.top()->key()` 没有被任何 range tombstone 所覆盖
- 在每次调用 SeekImpl() 后:
3. 对于所有 level `i` 和 `j <= i`,`range_tombstone_iters[j].prev.end_key() < children_[i].iter.key()`,也就是说 `range_tombstone_iters_[j]`当前是 level `j`中第一个 end key 大于等于 `children_[i].iter.key()` 的 tombstone。
4. 对于所有 level `i` 和 `j <= i`,如果 `j` 在 `active_`中,那么 `range_tombstone_iters[j]->start_key < children_[i].iter.key()`
- 当 `range_tombstone_iters[j]` 非 Valid() 时,我们考虑它的 prev 为最后一个 range tombstone
- 当涉及到 tombstone 的 start / end key 时,假设它的 value 为 `HeapItem::tombstone_pik`。 value 的 `op_type` 为 kMaxValid,这使得 tombstone keys 具有和 point keys 不一样的 value。
## HeapItem
- min/max heap 中的元素类型。每个 HeapItem 可以是一个 point iter 或者 range tombstone iter,取决于 `HeapItem::type`.
- 成员:
- **`iter`**: `IteratorWrapper`,point iterator
- **`level`**:`int`,iter 来自哪个 level,level 越小表示数据越新
- 因为 L0 的存在,这个是==虚拟== level,每个 L0 算一个 level,同一个 level 内数据有序
- **`tombstone_pik`**:`ParsedInternalKey`,tombstone 的 start 或 end key
- **`type`**:`Type`,三个枚举值
- `ITERATOR`
- `DELETE_RANGE_START`
- `DELETE_RANGE_END`
## 核心成员
- **`direction_`**:`Direction`,迭代方向:forward or reverse
- **`children_`**:`vector<HeapItem>`,所有需要 merge 的 child point iterators。
- *Invariant*:
- `children_[i]`在 `minHep_`中当且仅当 `children_[i].iter.Valid()`
- [ ] 最多一个 `children_[i]` 在 `minHeap_`中
- **`pinned_heap_item_`**:`vector<HeapItem>`
- 负责存储 range tombstone start 和 end keys 的 HeapItem
- heap 中的元素可能会指向它
- `pinned_heap_item_[i]`对应 `range_tombstone_iters_[i]`
- 即存储每个 level 当前的 tombstone,与 level 一一对应。
- *Invariant*:
- 分别设 `pinned_heap_item_[i]` 和 `range_tombstone_iters_[i]` 为 item 和 iter
- 当 `iter->Valid()` 时
- 如果 `item.type == DELETE_RANGE_START`,则 item 的 `tombstone_pik` 为 `iter->start_key()`
- 如果 `item.type == DELETE_RANGE_END`,则 item 的 `tombstone_pik` 为 `iter->end()`
- **`range_tombstone_iters_`**:`vector<unique_ptr<TruncatedRangeDelIterator>>`
- `range_tombstone_iters_[i]` 对应 `children_[i]`的 tombstone iter
- NULL 则表示该 child 没有 tombstones
- 如果为空,表示 merging 时不需要处理 range tombstones.
- *Invariant*:
- `pinned_heap_item[i]` 在 `minHeap_` 中当且仅当 `range_tombstone_iters_[i]->Valid()`
- [ ] 最多只有一个 `pinned_heap_item_[i]` 在 `minHeap_` 中
- **`active_`**:`set<size_t>`,哪些 levels 的 range tombstones 是 active 的。
- levels 从新到旧排序。
- *Invariant*:
- `i` 存在于 `active_` 中当前仅当 `range_tombstone_iters_[i]->Valid()` 以及`pinned_heap_item_[i].type == DELETE_RANGE_END`
- **`current_`**:`IteratorWrapper *`
- 当前迭代位置,当前堆顶的 child point iterator,用于返回当前 key() / value()
- 来自 `CurrentForward()/CurrentReverse()`.
- **`minHeap_`**:`BinaryHeap<HeapItem*, MinHeapItemComparator>`
- 按 每个 item 的 iter 当前 key 的顺序, 最小的 item 在堆顶,用于 forward iteration。
- 元素为 HeapItem 的==指针==,指向`children_`或 `pinned_heap_item_`中的元素。
- **`maxHeap_`**:`BinaryHeap<HeapItem*, MaxHeapItemComparator>`
- 按 HeapItem key 顺序的大顶堆,key 最大的在堆顶,用于 reverse iteration
- **`iterate_upper_bound_`**:`const Slice *`,来自 ReadOtions
- 只用于限制 range tombstones。point iterator 已在内部自己应用了该限制。
## Finish()
- MergingIteratorBuilder 结束后调用,此时所有的 iteartors 都已经添加完成。
- 该函数主要是 `range_tombstone_iters_` 来初始化 `pinned_heap_item_`。
- 过程:
- `pinned_heap_item_`跟 `range_tombstone_iters_` 大小一样
- 每个 `pinned_heap_item_` 元素的值初始化为:
- level 从 0 开始依次递增
- `tombstone_pik.type` 为 kTypeMaxValid
> [!note]- 使用 kTypeMaxValid 的原因
> tombstone 的 end key 是 exclusive 的。考虑 point internal key 跟 tombstone 的 start/end key 相同,使用 kTypeMaxValid 能保证 **start < end < point internal key**(kTypeMaxValid 最大,相同 ukey 和 seq 下顺序在最前面)。
>
> 实际上只有 TruncatedRangeDelIterator 中 truncated 后的 tombstone 需要这个,untruncated 的 tombstone key 会使用 kMaxSequenceNumber 和 kTypeRangeDeletion,已经能跟 point keys 区分开来。*见 TruncatedRangeDelIterator::start_key()/end_key()*
## AddToMinHeapOrCheckStatus(item)
- 将 child point iter 插入到 `minHeap_`
- 前提 iter 是 Valid 的,非 Valid 的话则检查其 status
## InsertRangeTombstoneToMinHeap()
- 参数:
- **`level`**:`size_t`
- **`start_key`**:`bool`,默认 true,表示插入 tombstone 的 start key,false 表示 end key
- **`replace_top`**:`bool`,默认 false,是否替换堆顶元素
- 将 `range_tombstone_iters[level]` 当前迭代位置的 tombstone 更新到`pinned_heap_item_[level]`,并插入到 min heap 中
- 如果插入的是 end key,则更新 `active_`
- **特例**:如果 tombstone 当前超过 `iterate_upper_bound_`(整体超过,即 tombstone start key 大于等于它),则不插入到 min heap,如果 `replace_top`为 true 则只进行 pop 操作。
## PopDeleteRangeStart()
- 如果 `minHeap_`的堆顶为 tombstone 的 start,则弹出,并插入 tombstone end 到 `minHeap_`中。该 tombstone 开始变成 active,需要更新 `active_`。
## SkipNextDeleted()
- 如果当前 key (min heap 堆顶)不应该返回给用户,就返回 true,这可能是因为以下三种情况:
- 当前 key 被某个 tombstone 覆盖
- 当前 key 是文件边界 sentinel key
- 当前 key 是 tombstone 的 end key
- 可能会根据需要 advance 堆顶 iter,并相应维护 heap 顺序和更新 `active_`。
- 调用前提:
- `minHeap_`非空,迭代方向为 kForward
- `minHeap` 堆顶不是 tombstone start
### DELETE_RANGE_END
- 即当前堆顶为 tombstone end key。此时当前 tombstone end key 是所有 iterators 中最小的,后续迭代的 keys 都会大于它,因此该 tombstone 已经使用完成,失效了,需要==清理==。
- 处理流程:
1. 从 `active_`中删除该 tombstone。
2. 删除堆顶即该 tombstone end
3. 移动该 tombstone 所属的 iter 到下一个 tombstone 位置(调用 Next )
4. 如果 tombstone iter 是 Valid 的话(即新 tombstone 有效),将其 start key 加入到 `minHeap_`中
5. 函数返回值: true
- 总结:删除当前 tombstone,插入同 level 下一个 tombstone。
### Sentinel Key
- 即当前堆顶为 point iter,但指向 sentinel key。
- 表示此时需要==切换==到同 level 的下一个文件,需要进行一些特殊处理。
- 如果 sentinel key 是一个 delete range tombstone,则对应的 tombstone 一定之前以后遇到过了。
- 因为放入 heap 中使用的 key type 更大,排序在前面
- 因此 `active_.count(current->level)` 必然为 0.
- 处理流程:
1. 弹出 `minHeap_` 堆顶元素(即该指向 sentinel key 的 child point iter)
2. heap 中还有当前文件残留未结束的 tombstone end key,进行清理
- 场景:文件最大 point key 和 tombstone end key 拥有相同的 user key,且该 user key 跨越两个文件。例如两个key分别为 `foo@100` 和 `foo@99`,tombstone end key 经过 truncated 后的 seqno 为前一个 key 的减一。
- 见 `TruncatedRangeDelIterator::TruncatedRangeDelIterator()`函数中对 largest key 的截断处理。
- 这也意味着这样的 tombstone end key 最多只有一个。
3. 调用 `child_iter->Next()`,将 LevelIterator 移动到一个新的文件;
4. 将新文件的 point iter 重新插入到 `minHeap_` 中
5. 将新文件 tombstone iter 的 首个 tombstone 插入到 heap 中(调用 `InsertRangeTombstoneToMinHeap()`)
- 总结:
- 处理文件切换相关逻辑:清理旧文件的 point iter & tombstone, 插入新文件的 point iter & tombstone。
- 该 case 下函数值为 true。
### Check active
- 即当前堆顶为 point iter,且指向非 sentinel;需要检查其当前 key 是否被 active tombstones 覆盖。
```mermaid
---
title: Check Cover
---
flowchart
classDef default font-size:10pt;
Start@{ shape: circle, label: "Start" } --> IsEmpty{active_ 是否为空}
IsEmpty -- 不为空 --> CompareLevel
CompareLevel{"cmp(active_begin,
current_level\)"}
IsEmpty -- 为空 --> ReturnFalse1
ReturnFalse1@{ shape: dbl-circ, label: "返回 false" }
CompareLevel -- 小于 --> LT[tombstone所属level 更新
当前 key 肯定被覆盖]
LT --> Seek[调用SeekImpl跳到
active tombstone end
即跳过该tombstone范围]
Seek --> ReturnTrue1@{ shape: dbl-circ, label: "返回 true" }
CompareLevel -- 等于 --> EQ[tombstone与当前
key的level 相同]
EQ --> CompareSeq{"cmp(key_seq,
tomb_seq)"}
CompareSeq -- 小于 --> LTKey[tombstone seq 更新,
key被覆盖]
NotCovered --> ReturnFalse3@{ shape: dbl-circ, label: "返回 false" }
LTKey --> Next[Next 当前 point iter,
弹出并重新插入 heap 中]
Next --> ReturnTrue2@{ shape: dbl-circ, label: "返回 true" }
CompareSeq -- 不小于 --> NotCovered[key seq 更新,
未被覆盖]
CompareLevel -- 大于 --> GT[active levels更旧
key 没有被覆盖]
GT --> ReturnFalse4@{ shape: dbl-circ, label: "返回 false" }
```
- `active_begin` 为 `active_.begin()`,活跃的最小/最新 tombstone level
- SeekImpl 仅移动大于等于 current level 的 levels。
- 即 tombstone active level 所能影响到的(比 active level 旧的)
## FindNextVisibleKey()
- 重复调用 `PopDeleteRangeStart()` 和 `SkipNextDeleted()` 直到堆顶为一个有效的 point key iter。
- 即堆顶 iter 指向一个 point key,且该 key 未被 tombstone 所覆盖。
- 或者 heap 为空没有更多的 keys。
## SeekImpl()
- 参数:
- **`target`**: `const Slice &`
- **`starting_level`**:`size_t`,默认值为 0
- *`range_tombstone_reseek`*:`bool`,默认为 false。
- target 是否来自 tombstone end key,表示来自 cascading seek,仅用于 perf 统计。
- 功能:
- 将所有 `levels >= starting_level`的 components 移动到 target 处或之后;
- components:point iters 和 tombstone iters
- 小于 `starting_level` 的(更新的)不动
- **调用场景**:SkipNextDeleted() 中发现了位于 level `i` 的 active tombstone,则需要将大于等于 `i` 的 iters 移动到 tombstone 之后(即跳过被其覆盖的部分),小于等于 `i`的更加新,不会被覆盖,因此无需移动
- 流程:
1. 清理两个 heap,`active_` 不动;
2. 添加小于 `starting_level` 的 child point iters;
- 直接重新放回 `minHeap_`中,不需要 advance iter
3. 添加小于 `starting_level`的 tombstones;
- 即将 `pinned_heap_item_[level]`放入 heap 中
- 优化:如果 level 位于 `active_`中,表示之前已经成功放入过堆中(未超过 `upper_bound`),则不需要越界检查,直接 push
4. 从 `active_`删除大于等于 `starting_level`的,因为下面要进行 reseek;
5. 处理大于等于 `starting_level`的 iters
- 将 point iter 和 tombstone iter 都 Seek 到 target,并插入到 heap 中
- 如果 seek 后的 tombstone start key 大于 target,则插入其 start key,否则插入其 end key(这种情况表示 tombstone 与 target 重叠,视为 active)。
- 优化:[cascading seek](https://github.com/facebook/rocksdb/pull/10449),不断更新 target,跳过已经被更新 level tombstone 覆盖的范围。
> [!info] cascading seek
> - 如果 target 与某 level 的 当前 tombstone 重叠,则更旧的层次需要同时跳过 target 和 tombstone end key,因为对于更旧的 level,小于 target 的部分无效,被当前 tombstone 覆盖的也无效,此时更新下次 seek target 为 tombstone end key,直接跳到 tombstone end key 之后。
<svg xmlns:xl="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="1381.1937 -61.94451 391.0175 74.95892" width="391.0175" height="74.95892">
<defs>
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="DimensionArrow_Marker" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-1 -9 15 18" markerWidth="15" markerHeight="18" color="black">
<g>
<path d="M 0 0 L 12.000001 0 M 12.000001 7.500001 L 12.000001 -7.500001 M 0 3.0000002 L 10.500001 0 L 0 -3.0000002" fill="none" stroke="currentColor" stroke-width="1"/>
</g>
</marker>
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="DimensionArrow_Marker_2" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-14 -9 15 18" markerWidth="15" markerHeight="18" color="black">
<g>
<path d="M 0 0 L -12.000001 0 M -12.000001 -7.500001 L -12.000001 7.500001 M 0 -3.0000002 L -10.500001 0 L 0 3.0000002" fill="none" stroke="currentColor" stroke-width="1"/>
</g>
</marker>
</defs>
<metadata> Produced by OmniGraffle 7.24.2\n2025-03-26 03:17:37 +0000</metadata>
<g id="Canvas_1" stroke-opacity="1" fill="none" fill-opacity="1" stroke-dasharray="none" stroke="none">
<title>Canvas 1</title>
<g id="Canvas_1_Layer_1">
<title>Layer 1</title>
<g id="Graphic_5">
<text transform="translate(1386.1937 -28.881592)" fill="black">
<tspan font-family="Helvetica Neue" font-size="16" fill="black" x="5684342e-20" y="15" xml:space="preserve">tombstone</tspan>
<tspan font-family="Helvetica Neue" font-size="16" fill="black" x="22.536" y="33.448" xml:space="preserve">start</tspan>
</text>
</g>
<g id="Graphic_6">
<text transform="translate(1690.1392 -28.881592)" fill="black">
<tspan font-family="Helvetica Neue" font-size="16" fill="black" x="5684342e-20" y="15" xml:space="preserve">tombstone</tspan>
<tspan font-family="Helvetica Neue" font-size="16" fill="black" x="25.048" y="33.448" xml:space="preserve">end</tspan>
</text>
</g>
<g id="Graphic_7">
<text transform="translate(1552.317 -28.881592)" fill="black">
<tspan font-family="Helvetica Neue" font-size="16" fill="black" x="3.92" y="15" xml:space="preserve">seek</tspan>
<tspan font-family="Helvetica Neue" font-size="16" fill="black" x="6679102e-19" y="33.448" xml:space="preserve">target</tspan>
</text>
</g>
<g id="Line_10">
<line x1="1435.0544" y1="-43.526283" x2="1720.5" y2="-43.50106" marker-end="url(#DimensionArrow_Marker)" marker-start="url(#DimensionArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
</g>
<g id="Graphic_12">
<path d="M 1576.4522 -45.224517 C 1577.7458 -43.503865 1577.6376 -41.080547 1576.1081 -39.551047 C 1574.4289 -37.871807 1571.6723 -37.905855 1569.951 -39.627113 C 1568.3832 -41.194877 1568.2152 -43.621617 1569.4666 -45.310817 L 1569.4521 -45.310996 L 1572.6333 -50.24677 C 1572.7267 -50.391677 1572.9218 -50.430507 1573.069 -50.3335 C 1573.1061 -50.309075 1573.1379 -50.27731 1573.1623 -50.24023 L 1576.4665 -45.22435 Z" fill="white"/>
<path d="M 1576.4522 -45.224517 C 1577.7458 -43.503865 1577.6376 -41.080547 1576.1081 -39.551047 C 1574.4289 -37.871807 1571.6723 -37.905855 1569.951 -39.627113 C 1568.3832 -41.194877 1568.2152 -43.621617 1569.4666 -45.310817 L 1569.4521 -45.310996 L 1572.6333 -50.24677 C 1572.7267 -50.391677 1572.9218 -50.430507 1573.069 -50.3335 C 1573.1061 -50.309075 1573.1379 -50.27731 1573.1623 -50.24023 L 1576.4665 -45.22435 Z" stroke="#84cd5c" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
<g id="Graphic_16">
<title>Clear</title>
<path d="M 1671.5355 -58.23076 L 1666.8234 -61.94451 L 1648.142 -47.221204 L 1629.4606 -61.94451 L 1624.7484 -58.23076 L 1643.4298 -43.507454 L 1624.7484 -28.784147 L 1629.4606 -25.070397 L 1648.142 -39.793704 L 1666.8234 -25.070397 L 1671.5355 -28.784147 L 1652.8541 -43.507454 Z" fill="#ccc"/>
</g>
</g>
</g>
</svg>
## CurrentForward()
- 返回当前`minHeap_`堆顶的 point iter。空的话返回 nullptr.
- 前提:堆顶元素(如果非空)为 ITERATOR(非 tombstone)。
## Seek()
1. 调用 SeekImpl(), `starting_level`为0,所有 levels 都需要 reseek。
2. 调用 FindNextVisibleKey(), 确保堆顶为有效 point iter。
3. 更新 `current_` 为 CurrentForward(),即堆顶 point iter。
## Next()
1. 调用 `current_->Next()`,并将 `current_`弹出再重新插入到 `minHeap_` 中;
2. 调用 FindNextVisibleKey(), 确保堆顶为有效 point iter;
3. 更新 `current_` 为 CurrentForward()
## 总结
- 使用 `minHeap` 维持堆顶为 children 中最小的 iter,实现合并逻辑;
- tombstone 处理:
1. 先把 tombstone start key 放入 minHeap 中,当其变为堆顶时,表示 tombstone 开始激活,pop start key 后再将 tombstone end key 放入 minHeap 中。
2. tombstone 激活后,迭代 point keys 时需要检查是否被 tombstone 覆盖;
3. 当 tombstone end key 变为堆顶时,表示该 tombstone 已经失效,后续的 point keys 都会超过它,将其 pop 出堆并从 active 中删除。
---
# Iterator iterface
- 迭代的 item 面向 user key(即不带 seq 和 type)
- 关键方法:
- Valid, status()
- SeekToFirst,SeekToLast,Seek,SeekForPrev
- Next,Prev
- key(), value()
---
# DBIter
- 继承自 Iterator。
- 负责将 InternalIterator 产生的 internal keys 转换为 user keys。
- 处理 key 多版本、deletion markers 、merge 等逻辑。
```mermaid
flowchart LR
InternalIter@{ shape: win-pane, label: "InternalIterator" }
DBIter
User@{ shape: lean-l, label: "User" }
InternalIter -- internal keys --> DBIter
DBIter -- user keys --> User
```
## 核心成员
- **`iter_`**:`IteratorWrapper`,input InternalIteator
- **`version_`**:`Version *`,迭代器创建时当前 Version,构造时传入
- **`sequence_`**:`SequenceNumber`,最大可见 seq。
- 来自 `ReadOptions.snapshot` 或者当前 Version 的 LastSequence()
- **`max_skippable_internal_keys_`**: `uint64_t`
- 来自 ReadOptions,跳过太多 keys 时返回 Incomplete,默认为0表示不启用。
- **`saved_key_`**:`IterKey`
- 存储当前迭代位置的 user key,即 key() 函数会返回 `saved_key_.GetUserKey()`
- 但在 Seek/Next 等函数的调用过程中,可能会指向 delete marker 等
- **`value_`**:`Slice`,存储当前迭代位置的 value,用于 value() 函数返回。
## ParseKey()
- 解析 `iter_.key()` 到 `ikey_` 中,失败则设置 ==`valid_` ==为 false,同时填充 `status_`;
- 返回值:bool,是否成功
## FindNextUserEntryInternal()
- 从当前 `saved_key_`位置开始,查找下一个有效的 user entry(可供迭代器返回的)
- 参数:
- **`skipping_save_key`**:`bool`,是否跳过 `saved_key_` 中保存的 user key
- 典型场景:`saved_key_` 保存了当前迭代位置的 user key,DBIter Next() 时就需要跳过它,找下一个不同的 user key。
- **`prefix`**:`const Slice *`,不为 NULL 表示 prefix scan
- 循环迭代底层 `iter_`,跳过无效的 internal keys
- 如 seq 不可见、delete marker
- `skipping_save_key` 逻辑,跳到下一个 user key
```mermaid
stateDiagram-v2
[*] --> ReadIkey
ReadIkey: 读取 iter_->key() 到 ikey_
ReadIkey --> IsVisible
IsVisible: 当前 ikey_ 是否对 sequence_ 可见
state VisibleState <<choice>>
IsVisible --> VisibleState
VisibleState --> SkipSaved1: 可见
VisibleState --> UpdateUserKey: 不可见
SkipSaved1: 是否需要跳过 saved_key_(skipping_saved_key 为 true 且 ikey_.user_key 小于等于 save_key_ 的 user key)
state SkipState <<choice>>
SkipSaved1 --> SkipState
SkipState --> DoSkip: 是
DoSkip: 跳过当前 internal entry,num_skipped++
SkipState --> CheckType: 否
CheckType: 检查 ikey_.type
state TypeState <<choice>>
CheckType --> TypeState
TypeState --> DeleteType: kTypeDeletion | kTypeSingleDeletion
DeleteType: 跳过当前 user key(将 ikey_.user_key 存入 saved_key,并设置 skipping_saved_key)
TypeState --> ValueType: kValue
ValueType: 找到有效 user entry,存入 saved_key_ 和 value_,结束
ValueType --> [*]
UpdateUserKey: 跳过该 internal entry,选择性更新 saved_key_
state join_state <<join>>
UpdateUserKey --> join_state
DeleteType --> join_state
DoSkip --> join_state
join_state --> AdvanceIter
State AdvanceIter {
CheckReseek: 是否需要 reseek
state ReseekState <<choice>>
CheckReseek --> ReseekState
ReseekState --> DoReseek: 需要
ReseekState --> NextIter: 不需要
NextIter: iter->Next()
DoReseek: iter.Seek(reseek_key)
state join_state2 <<join>>
NextIter --> join_state2
DoReseek --> join_state2
join_state2 --> CheckValid
CheckValid: 检查 iter_->Valid()
}
AdvanceIter --> ReadIkey: 下一轮循环
```
- `num_skipped`:衡量相同的 user key 下,跳过了多少个它的 intenal entries,超出 cf 配置则进行 **reseek**(相对于顺序性扫描并跳过,reseek 可能更高效)。
- `reseek_key`(internal key)的确定:
- 如果 `skipping_saved_key` 为 true,设为 `(saved_key_.user_key, 0, kTypeDeletion)`
- 即同 user key 最后一个 seq 和 type(最小值)
- 否则,设为 `(saved_key_.user_key, sequence_, kValueTypeForSeek)`
- 即同 user key 第一个可见的 internal key
- 每次循环都会进行 TooManyInternalKeysSkipped() 检查:如果循环总次数超过 `ReadOptions.max_skippable_internal_keys`, DBIter 以 Incomplete 状态失败。
## SeekToFirst()
1. 调用 `iter_->SeekToFirst()`
2. 设置 `save_key_` 为 `iter_->key()`
3. 调用 FindNextUserEntry(), 不跳过 `saved_key_`
## SetSavedKeyToSeekTarget(target)
- 将 target user key 设置到 `saved_key_` 中(设置为 internal key 格式)
- user key: `max(iterate_lower_bound_, target)`
- 确保不小于 `iterate_lower_bound_`
- seq: `sequence_`,即创建 iter 时指定的
- type: `kValueTypeForSeek`
## Seek(target)
1. 调用 SetSavedKeyToSeekTarget 将 target 存入 `saved_key_`
2. Seek 底层 iter:`iter_->Seek(saved_key_)`
3. FindNextUserEntry(), 不跳过 `saved_key_`
## Next()
1. Advance 底层 iter:`iter->Next()`
2. FindNextUserEntry(), 跳过 `saved_key_`(需要找下一个跟当前不同的 user key)
## 总结
- 消费 internal enties,返回 user entries
- 处理同 key 多版本、seq 可见性、delete marker 等逻辑
- 同一个 user key 只返回一个最新可见的版本
- reseek 优化
---
# ArenaWrappedDBIter
- 将 DBIter,MergingIterator 及其引用的其他内部 Iterator 都分配在一起,对缓存更友好。
-------
# DBImpl
## NewIterator()
- 创建整个 DB 的 user iterator。
- 流程:
1. 获取并引用 sv
2. 确定读最大可见 seq,来自快照或者 LastSequence)
3. 调用 DBImpl::NewInternalIterator() 创建 DB 级别的 internal iter(MergingIterator)
- 创建 MergingIterator,依次添加以下 child internal iters
- memtable
- immutable memtables
- L0 每个文件创建一个 TableReader iter
- 其余每层创建一个 LevelIterator
- MergingIterator 绑定 sv,实现迭代器析构后 Unref sv(CleanupSuperVersionHandle)
4. 基于 internal iter 创建 DBIter
- 使用 ArenaWrappedDBIter 优化,让 DBIter、所有 internal iters 都分配在同一段连续空间。
----
# TODO
- [ ] readahead_size / prefetch 实现
- [ ] PinnedIteratorsManager
- [x] 迭代器如果防止 memtable 不会被释放
- 引用 sv,memtable 是在其所属的 sv 无引用后才释放。
- [ ] MergingIterator backward 方向的实现
- [ ] DBIter 对 kTypeMerge 处理
- [ ] DBIter backward 方向迭代
## Prefix scan
- [ ] total_order_seek 和 auto_prefix_mode 实现
- [ ] `auto_prefix_mode`
- [ ] [PR: Add ReadOptions.auto_prefix_mode #6314](https://github.com/facebook/rocksdb/pull/6314)
## 实验
- 测试 prefix seek 行为
- [ ] 生成一个只有 tomstones 的 SST