# 1. Format
## Key
- 通过 Key 里面的 Type 来判断是 blob index 还是普通 KV
## BlobIndex/Value
```
+------+-------------+----------+----------+-------------+
| type | file number | offset | size | compression |
+------+-------------+----------+----------+-------------+
| char | varint64 | varint64 | varint64 | char |
+------+-------------+----------+----------+-------------+
```
- Type:
- kBlob = 1
- Encode 成 String 后,作为 value 写入 SST
----
# 2. Version
## FileMetaData
- 新增成员:`oldest_blob_file_number` ,表示引用的最老的 blob file 的编号
- 更新 `oldest_blob_file_number`:
- 写入 KV 时,判断 value 如果是 blob index,解析出 blob file number,尝试更新
```c++
Status FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
SequenceNumber seqno,
ValueType value_type) {
// ...
if (oldest_blob_file_number == kInvalidBlobFileNumber ||
oldest_blob_file_number > blob_index.file_number()) {
oldest_blob_file_number = blob_index.file_number();
}
}
```
## SharedBlobFileMetaData
- 文件:db/blob/blob_file_meta.h
- 代表 blob files 不可变的元数据
- 核心成员:
- **blob_file_number_** // 文件编号
- **total_blob_count_** // 文件内有多少个 blobs
- **total_blob_bytes_** // 文件内 blob 的总字节数
- **checksum** 信息
## BlobFileMetaData
- 文件:db/blob/blob_file_meta.h
- 核心成员:
- **shared_meta_** // SharedBlobFileMetaData
- **linked_ssts_** // 关联了哪些 SSTs(文件编号), 该 blob file 是哪些 SSTs 的 oldest blob file
- **garbage_blob_count_**
- **garbage_blob_bytes_**
## BlobFileMetaDataDelta
- 代表 BlobFileMetaData 上的变更
- 核心成员:
- **additional_garbage_count_**
- **additional_garbage_bytes_**
- **newly_linked_ssts_** // std::unordered_set<uint64_t>
- **newly_unlinked_ssts_** // std::unordered_set<uint64_t>
## Blob File Linked SSTs 维护
- 为 blob file 添加 Linked SSTs:
- `VersionBuilder::ApplyFileAddition()`
- 即新增 SST 时,读取 SST 的 oldest_blob_file_number,获取目标 blob file
- 为 blob file 删除 linked SSTs:
- `VersionBuilder::ApplyFileDeletion()`
- 即删除 SST 时
--------
# 3. 核心逻辑
## Blob File 生成
- 构建类: db/blob/blob_file_builder.cc::BlobFileBuilder
- 生成时机:
- compaction 或者 flush 输出 KV 时(未来会写入到 SST)
- 关键文件:db/compaction/compaction_iterator.cc
- 关键函数: `CompactionIterator::ExtractLargeValueIfNeeded()`
- 遇到 key type 为 kValue 时调用
- 在 builder 中判断是否是大 value
- 切换文件:
- BlobFileBuilder::CloseBlobFileIfNeeded()
- 检查文件大小是否超出 `blob_file_size` 的配置
## Relocate
- pr: [[rocksdb-blobdb-prs#^2b538c]]
- 只在 compaction 时触发,关键文件:db/compaction/compaction_iterator.cc
- 输出 KV 时(`CompactionIterator::PrepareOutput`) 遇到 **BlobIndex** 时调用
- 核心方法:`CompactionIterator::GarbageCollectBlobIfNeeded()`
- 只 relocate blob index 的 file number 在 age cut off 范围内的
- relocate 逻辑就是读出 blob 写入到新文件(会重新判断是否需要分离大Value)
## GC统计
- pr: [[rocksdb-blobdb-prs#^c3365f]]
- compaction 迭代 input 时遇到 BlobIndex 增加 inflow,输出写入到SST时增加 outflow
- inflow - outflow 就是垃圾的数量
- 代表原 BlobIndex 没有被写回 LSM,可能的原因:
- 被丢弃(被覆盖,重复version)
- relocate 到新的 blob
- relocate 到 SST(blobdb的配置发生变更)
- 统计:`db/blob/blob_garbage_meter.h/cpp`
- 增加 inflow: `db/blob/blob_counting_iterator.h`,包装 InternalIterator 来实现
- 增加 outflow :`CompactionOutputs::AddToOutput()`
## Force GC
- pr: [[rocksdb-blobdb-prs#^bc3d52]]
- 挑选一批 垃圾比例高的 blob files, 根据 linked SSTs,将目标 SSTs 标记为需要 GC
- db/version_set.cc: `VersionStorageInfo::ComputeFilesMarkedForForcedBlobGC`
- 只考虑在 age cut off 内的 blob files
- 对应的 compaction reason 为 `kForcedBlobGC`(“ForcedBlobGC”)
- [ ] 中间的 batch 垃圾比例比较高,如何能被挑选上
----
# 4. 统计信息
## Properties
- `db->GetProperty("<name>")`
| name | 含义 |
| :-----------------------------------------|: --------------------- |
|blob-stats | 当前 version 所有 blob files 的个数、大小、垃圾大小、空间放大比例 |
|num-blob-files | 当前 version 中 blob files 的个数|
|total-blob-file-size | 所有 versions 的 blob files 的大小字节数总和 |
|live-blob-file-size | 当前 version 的 blob files 的大小字节数总和 |
|blob-cache-capacity | - |
|blob-cache-usage | -|
|blob-cache-pinned-usage| - |
|sstables| 同时返回每层的 SST files 和 blob files 的元数据|
## Ticker
- [BlobDB Ticker enums](https://github.com/facebook/rocksdb/blob/791a7fe4023ac795072a9e0cbfc679a3764a2ba0/include/rocksdb/statistics.h#L232)
## Histogram
- [BlobDB specific stats](https://github.com/facebook/rocksdb/blob/v8.3.3/include/rocksdb/statistics.h#L566)