# 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)