# MemTable
## ImmutableMemtableOptions
- **`memtable_prefix_bloom_bits`**:`uint32_t`,布隆过滤器大小
- 等于 `write_buffer_size * memtable_prefix_bloom_size_ratio`
- 虽然叫 prefix,也涵盖了 key 整体的 filter
- 默认 0, 表示不启用 memtable 的过滤器
- **`memtable_whole_key_filtering`**:`bool`,来自 `AdvancedColumnFamilyOptions`
- 布隆过滤器是否包含了 whole keys(相对于 key prefixs) ,默认 false
- **`arena_block_size`**: `size_t`,来自 `AdvancedColumnFamilyOptions`
- **`memtable_huge_page_size`**:`size_t`, 来自 `AdvancedColumnFamilyOptions`
- 配置大于 0 表示启用,会先从 huge page 中分配内存(包含 arena 和 bloom filter)
- 默认为 0 不启用,从 malloc 分配内存。
<br>
- **`protection_bytes_per_key`**:来自 `AdvancedColumnFamilyOptions`
- 每 KV 额外存储 checksum,防止内存损坏,默认不启用
- **`inplace_update_*`**: inplace update 相关配置,默认不启用
- 启用后无法保证快照语义。见 `AdvancedColumnFamilyOptions::inplace_update_support` 的说明。
## MemTable 关键成员
- **`id_`**:cf 内自增的 id
- **`table_`**:`unique_ptr<MemTableRep>`,存储普通 KV 数据
- **`range_del_table_`**:`unique_ptr<MemTableRep>`,存储 range deletions
- **`bloom_filter_`**:`unique_ptr<DynamicBloom>`,布隆过滤器,可能为空
- **`first_seqno_`**:`atomic<SequenceNumber>`, 第一个 Insert 的 KV 的 seqnum
- **`earliest_seqno_`**:`atomic<SequenceNumber>`,创建时 DB 的 seqnum
- 等于 `kMaxSequenceNumber` 表示未设置
- 设置后,insert 到此 memtable 的数据保证大于等于它
- **`write_buffer_size_`**: `atomic<size_t>`,memtable 大小,当没配置过滤器时可动态更改
- **`arena_`**: `ConcurrentArena`,给数据、过滤器分配内存。
- **`flush_state_`**:`atomic<FlushStateEnum>`
- 三个枚举值:
- `FLUSH_NOT_REQUESTED`:不需要 flush
- `FLUSH_REQUESTED`:需要 flush
- `FLUSH_SCHEDULED`:已经放入 FlushScheduler
- **`fragmented_range_tombstone_list_`**:`unique_ptr<FragmentedRangeTombstoneList>`
- [Fragment memtable range tombstone in the write path](https://github.com/facebook/rocksdb/pull/10380/)
- 变成 immutable 时生成,用于加速 range tombstones 的查询
- **`cached_range_tombstone_`**:
- [Cache fragmented range tombstone list for mutable memtables](https://github.com/facebook/rocksdb/pull/10547)
- **`mem_next_logfile_number_`**:`uint64_t` ^ce1bad
- 当 memtable 冻结时(SwitchMemtable中),创建的新 WAL。
- 当 memtable flush 后,编号小于它的 WAL 就不要 recover 了。
- 比当前 memtable 新的数据只会写入到大于等于 `mem_next_logfile_number_` 的 WAL 中
## BatchPostProcess()
- 仅用于并行写 Memtable 时,每个 writer / batch 累积自己的写入计数到 `MemTablePostProcessInfo`,batch 写完后将计数一次性纳入到 Memtable 的计数中
- 内部会 `UpdateFlushState()` 检查和更新 flush 状态
## ShouldFlushNow()
- 检查 memtable 是否应该 flush。
- **Flush 条件**:
- `num_range_deletes_` 大于 `memtable_max_range_deletions_`选项限制
- 选项来自 `ColumnFamilyOptions`,默认为0,表示无限制
- [#11358](https://github.com/facebook/rocksdb/pull/11358)
- 已分配的内存大于阈值
- 这个阈值略大于 `write_buffer_size`,因为 arena 会多分配
- [ ] 分析 heuristic way:
> flush when arena has allocated its last and the last is "almost full"
## UpdateFlushState()
- 通过 `ShouldFlushNow()` 判断是否需要 Flush,如果需要,则将 `flush_state_` 设置为 `FLUSH_REQUESTED`
- 调用时机:
- 非并行写 Memtable 时,每次 Add 写入时调用
- 并行写 Memtable 时,每个 batch 写完后通过 `BatchPostProcess`调用
## ShouldScheduleFlush()
- 返回 true 如果 `flush_state_` 为 `FLUSH_REQUESTED`
## MarkFlushScheduled()
- CAS 将 `flush_state_` 从 `FLUSH_REQUESTED` 变为 `FLUSH_SCHEDULED`,返回是否成功。
-------
# MemTableInserter
- 继承了 `WriteBatch::Handler` ,用于遍历 WriteBatch,插入到 Memtable 中。
## 核心成员
- **`mem_post_info_map_`**:`Map<Memtable*, MemTablePostProcessInfo>`
- 缓存插入过程中每个 Memtable 的计数(`data_size`,`num_entries`,`num_deleted`等)
- 插入完成后,统一处理。
- **`cf_mems_`**: `ColumnFamilyMemTables*`,来自 DBImpl,用于获取各个 cf 当前的 memtable
- **`flush_scheduler_`**: `FlushScheduler*`,来自 DBImpl
- **`trim_history_scheduler_`**:`TrimHistoryScheduler *`,来自 DBImpl
## PostProcess()
- 当 `concurrent_memtable_writes`为 true 时,batch insert 完后调用
- 针对 `mem_post_info_map_`中的每个 memtable 调用 `Memtable::BatchPostProcess`
- 累积计数
- 检查是否需要 flush
## CheckMemtableFull()
- 检查当前 cf 的 memtable,每次写入(put/delete)时调用。
- memtable 是否需要 flush:
- 调用 `Memtable::ShouldScheduleFlush()`
- 如果需要,将当前 cf 加入到 `flush_scheduler_`队列
- 检查 memtable list 是否超过 `max_write_buffer_size_to_maintain`
- transaction 相关,参数默认为0,表示不检查 [#5022](https://github.com/facebook/rocksdb/pull/5022)
- 超过后,将当前 cf 加入到 `trim_history_scheduler_`队列
---
# MemTableListVersion
- 某一时刻的 immutable memtables 列表。
- 该类非线程安全。需要外部进行同步控制(例如通过 db mutex 或者在 write thread 内操作)。
## 关键成员
- **`memlist_`**:`std::list<MemTable*>`,没有 flush 的 immutable memtables
- ==新==加入的在链表头部
- **`memlist_history_`**:`std::list<Memtable*>`,已经 flush 的 memtables,额外保留它们用于事务检测写冲突。
- 新加入的在链表头部
## Remove()
- 从 `memlist_` 中删除一个 memtable
- 从链表移除,将 memtable 标记为 Flushed(`m->MarkFlushed()`)
- 如果需要维护 history(事务需要),则放入 `memlist_history_`,否则进行 Unref
- Unref 为 0 则放其入待删除列表
## Add()
- 往 `memlist_` (前面)添加一个 memtable
------
# MemtableList
- 存储了对所有 immutable memtables 的引用。
- 如果有多个,可以并行 flush,但写入 manifest 时需要 FIFO 有序(recover需要)。
- 除了部分原子变量,该类非线程安全(需要 db mutex 保护或者在 write thread 内操作)。
- 与 MemTableListVersion 的关系:
- MemTableListVersion 相当于 Version
- MemtableList 相当于 VersionSet(多版本)
## 关键成员
- **`current_`**: `MemTableListVersion *`,最新版本的 memtable list
- **`imm_flush_needed`**:`atomic<bool>`,是否需要 flush
- **`imm_trim_needed`**:`atomic<bool>`,是否需要 trim history
- **`num_flush_not_started`**:`int`,还没有开始 flush 的 memtables 数量
- **`commit_in_progress_`**: `bool`,是否正在提交 flush 的结果
- **`flush_requested_`**:`bool`,标记,外部请求对 memtable 进行 flush
- **`current_memory_usage_`**:`size_t`,当前的内存使用量
## IsFlushPending()
- 返回 true,如果有 memtable 需要 flush,但还没开始。
- 条件(需要同时满足):
- `flush_requested_`为 true
- `num_flush_not_started_ > 0`
- `num_flush_not_started_ > min_write_buffer_number_to_merge`
## IsFlushPendingOrRunning()
- 是否有 memtable 需要被 flush 或者正在 flush
## PickMemtablesToFlush()
^b51817
- 入参:
- **`max_memtable_id`**: `uint64_t`,只挑选小于此 ID 的 memtables
- 从 `current_->memlist_`的末尾开启遍历挑选,满足以下条件
- id 小于等于 `max_memtable_id`
- memtable 的 `flush_in_progress_`标志为 false(即没有正在被 flush)
- 避免挑选不连续的 memtables(比如两个链表元素中间有个不满足条件的,就停止挑选)
- 被挑选的 memtable 会设置其 `flush_in_progress_`标志为 true
- 每次调用都会选择最旧的还没 flush 的 memtable。
## InstallNewVersion()
- 修改 memtable list 的前置操作,多版本下的写时拷贝。
- 从当前 `current_` 克隆创建新的 MemTableListVersion,设置为新的 `current_`
- 如果 `current_` 的引用为 1, 则表示只有自己持有,不需要拷贝
## Add()
- 入参:`Memtable *m`
- 将一个 memtable 加入到 immutable list
- 操作流程
1. `InstallNewVersion()`克隆 `current_`
2. 将 m 加入到 `current_->memlist_`
3. 将 m 表示为 immutable: `m->MarkImmutable()`
4. `num_flush_not_started_` 加 1,如果是 从 0 加到 1,标记 `imm_flush_needed` 为 true
## RemoveMemTablesOrRestoreFlags()
- flush 完写入到 MANIFEST 后调用
- 操作的 memtables 范围:`memlist_`末尾的 `batch_count` 个(对应提交的范围)
- 根据提交操作返回的 status 做不同的操作:
- 如果 status 是 ok 的:从 `memlist_` 中移除这些 memtables
- 否则:恢复它们的 `flush_completed_` 等标记,以便可以再次 flush
## TryInstallMemtableFlushResults()
^d8e92e
- flush 成功后提交给 manifest,并删除被 flush 的 imm
- 有可能其他线程也在提交,此时就会直接返回成功。
- 入参:
- **`mems`**: `vector<Memtable*> &`,flush 成功的 memtables
- **`file_number`**:`uint64_t`,flush 生成的 SST 文件编号
- 函数进入时已经持有 db mutex,中间`LogAndApply`内会释放后再重新获取。
- 执行流程:
1. 依次设置各个 `mems`的 `flush_completed_` 标记和 `flie_number_`字段
- 即使因为冲突自己没提交成功,其他线程看到这些状态也能代替提交
2. 检查和设置 `commit_in_progress_` 标记
- 如果之前为 true,则表示已经有其他线程在 commit,直接返回。
3. 从 `current_->memlist_`末尾(从旧到新)提交已经 flush 完成的 memtable
- 如果有 memtable 的 `flush_completed_` 为 false,则退出(由其他线程提交)
- 必须从旧到新==有序提交==
- 调用 `VersionSet::LogAndApply()` 提交给 Manifest
4. 提交后调用 `RemoveMemTablesOrRestoreFlags()`,成功则删除 memtables,否则取消相关标记,重试 flush。
## InstallMemtableAtomicFlushResults()
---
# WriteBufferManager
- 用于实现多 CF,多 DB 整体的内存使用限制。
- 可以通过 `DBOptions::write_buffer_manager` 为多个 DB 实例指定同一个 manager。
- 默认:一个 DB 一个 WriteBufferManager
- `db/db_impl/db_impl_open.cc` SanitizeOptions 函数中创建
- 创建时的关联配置:`DBOptions::db_write_buffer_size`
- 默认为 0 ,表示内存无限制。
-------
# TODO
- [ ] inplace_update_support
- [x] SwitchMemtable
- leader writer 调用 `DBImpl::PreprocessWrite()` 时
- [ ] https://github.com/facebook/rocksdb/pull/6146
- memtable 的释放是否在后台,内存占用是否可能增加,内存统计是否能看到这部分内存
- [ ] Memtable merge
- [ ] hint_per_batch
- [x] skyline: https://rocksdb.org/blog/2018/11/21/delete-range.html
- [ ] trim history 的调用时机,flush job中是否会调用
- [ ] mempurge
- https://github.com/facebook/rocksdb/pull/8454 (prototype)
- https://github.com/facebook/rocksdb/pull/8628
- https://github.com/facebook/rocksdb/pull/8656