master 76e3f68be2d0 cached
58 files
183.3 KB
70.7k tokens
1 requests
Download .txt
Showing preview only (258K chars total). Download the full file or copy to clipboard to get everything.
Repository: rsy56640/read_and_analyse_levelDB
Branch: master
Commit: 76e3f68be2d0
Files: 58
Total size: 183.3 KB

Directory structure:
gitextract_jqr4w8rl/

├── README.md
├── architecture/
│   ├── DB/
│   │   ├── Builder - 2018-10-03 - rsy.md
│   │   ├── DBImpl - 2018-11-14 - rsy.md
│   │   ├── DBIter - 2018-11-14 - rsy.md
│   │   ├── Log - 2018-11-08 - rsy.md
│   │   ├── Manifest & VersionEdit - 2018-11-07 - rsy.md
│   │   ├── Memtable - 2018-10-04 - rsy.md
│   │   ├── README.md
│   │   ├── Recover - 2018-11-08 - rsy.md
│   │   ├── Slice - 2018-09-16 - rsy.md
│   │   ├── Snapshot - 2018-11-07 - rsy.md
│   │   ├── Status-2018-10-02-dz.md
│   │   ├── TableCache - 2018-09-30 - rsy.md
│   │   ├── ValueType & SequenceNumber 2018-11-07 - rsy.md
│   │   ├── Version & VersionSet - 2018-11-12 - rsy.md
│   │   ├── WriteBatch - 2018-10-01 - rsy.md
│   │   └── dbformat_key-2018-10-01-ss.md
│   ├── SSTable/
│   │   ├── Block - 2018-10-02 - rsy.md
│   │   ├── BlockHandle&Footer - 2018-10-03 - rsy.md
│   │   ├── Compaction - 2018-10-05 - rsy.md
│   │   ├── FilterBlock - 2018-10-03 - rsy.md
│   │   ├── FilterPolicy - 2018-10-03 - rsy.md
│   │   ├── Iterator - 2018-10-01 - rsy.md
│   │   ├── Iterator_Wrapper - 2018-10-03 - rsy.md
│   │   ├── LSM - 2018-10-06 - rsy.md
│   │   ├── MergingIterator - 2018-10-05 - rsy.md
│   │   ├── README.md
│   │   ├── Table - 2018-10-04 - rsy.md
│   │   └── TwoLevelIterator - 2018-10-03 - rsy.md
│   ├── util/
│   │   ├── Arena/
│   │   │   └── arena - 2018-09-30 - rsy.md
│   │   ├── BloomFilter/
│   │   │   └── BloomFilter - 2018-10-10 mk.md
│   │   ├── Coding/
│   │   │   └── coding - 2018-09-06 - rsy.md
│   │   ├── Comparator/
│   │   │   └── Comparator - 2018-10-10 - mk.md
│   │   ├── Env_Options/
│   │   │   ├── Env - 2018-10-04 -jyh.md
│   │   │   ├── Options - 2018-10-04 - jyh.md
│   │   │   └── README.md
│   │   ├── LRU/
│   │   │   └── cache - 2018-09-20 - rsy.md
│   │   ├── Logging/
│   │   │   ├── Logging - 2018-10-14 - jyh.md
│   │   │   └── README.md
│   │   ├── README.md
│   │   ├── memenv/
│   │   │   └── README.md
│   │   └── memtable/
│   │       ├── README.md
│   │       └── skiplist_2018_10_12_ss.md
│   └── 平台相关-锁-信号-原子-压缩/
│       ├── README.md
│       ├── atomic/
│       │   └── README.md
│       ├── posix/
│       │   └── README.md
│       └── snappy/
│           └── README.md
├── doc/
│   ├── SA.md
│   ├── Understand配置及使用/
│   │   └── README.md
│   ├── plan.md
│   ├── 文档格式模板.md
│   ├── 文档格式说明.md
│   ├── 演讲10-10.pptx
│   ├── 演讲11-21.pptx
│   ├── 第十四组 - 进度报告.md
│   └── 软件体系结构.md
└── reference/
    ├── LevelDB源码分析.docx
    └── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: README.md
================================================
# read_and_analyse_levelDB
LevelDB 源码剖析


================================================
FILE: architecture/DB/Builder - 2018-10-03 - rsy.md
================================================
# Builder - 2018-10-03 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)


    
<a id="module_info"></a>
## 模块信息

`db/builder.h`, `db/builder.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

`BuildTable()`:为 level-? 创建 `Table`,并把 metadata 存到 `FileMetaData* meta`


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

由 `TableBuilder` 将 `MemtableIterator` 的数据依次写入 sstable,并将新生成的 sstable 加入 TableCache。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

    extern Status BuildTable(const std::string& dbname,
                             Env* env,
                             const Options& options,
                             TableCache* table_cache,
                             Iterator* iter,
                             FileMetaData* meta);

`dbname` -> `fname` -> `WritableFile*` -> `TableBuilder*` -> 循环 `iter`,`TableBuilder::Add(iter->key(), iter->value())` -> `TableBuilder::Finish()`

================================================
FILE: architecture/DB/DBImpl - 2018-11-14 - rsy.md
================================================
# DBImpl - 2018-11-14 - rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`include/leveldb/db.h`, `db/db_impl.h`, `db_impl.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/DB_UML_11_15.png)   
![](assets/DBImpl_UML1_11_15.png)   
![](assets/DBImpl_UML2_11_15.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `DB::Open()`:用户调用,**启动 db**,并准备环境
- `DestroyDB()`:用户调用,**删除 db**

<a></a>

- `DBImpl::NewDB()`:建立新 db,写 manifest 和 CURRENT
- `DBImpl::DBImpl()`:准备参数等资源
- `DBImpl::~DBImpl()`:用户调用 delete db,**关闭 db**

<a></a>

- `DBImpl::Get()`:用户调用,**查找**给定 key 的 value
- `DBImpl::Put()`:用户调用,**插入** k-v(就是 `DB::Put()`)
- `DBImpl::Delete()`:用户调用,**删除** k-v(就是 `DB::Delete()`)
- `DBImpl::Write(WriteBatch*)`:Put / Delete 底层的最终实现。**写 log,写 memtable**
- `DBImpl::NewIterator()`:用来遍历 db(调用时的 version)
- `DBImpl::GetSnapshot()`:用户调用,获得当前 sequence 所标记的快照
- `DBImpl::ReleaseSnapshot()`:用户调用,释放不用的快照
- `DBImpl::CompactRange()`:用户 **手动触发 compaction**。针对 range 来进行 compaction

<a></a>

- `DBImpl::Recover()`:恢复 db 状态并且将恢复对于 version 日志操作
- `DBImpl::RecoverLogFile()`:对单个 log 进行回放,期间可能会执行 compaction

<a></a>

- `DBImpl::NewInternalIterator(SequenceNumber*)`:收集当前版本所有可用 iterator,产生一个 merging iterator(参数后面说)
- `DBImpl::BuildBatchGroup()`:整合一部分 WriteBatch(一部分是为了控制 size)
- `DBImpl::DeleteObsoleteFiles()`:回收每次 compaction 之后被废弃的文件
- `DBImpl::MakeRoomForWrite()`:如果 mem 空间不足,compaction 并创建新 mem(如果 level-0 文件过多就阻塞)
- `DBImpl::WriteLevel0Table(MemTable*, VersionEdit*, Version*)`:**将 memtable dump 成 sstable,不一定是 level-0**(参考 [VersionSet](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Version%20%26%20VersionSet%20-%202018-11-12%20-%20rsy.md));对于 Version 的修改记录在 `VersionEdit*` 里面
- `VersionSet::LogAndApply()`:应用 VersionEdit 增量,**写入 Manifest 文件**,每次 compaction 时调用
- `DBImpl::CompactMemTable()`:调用 `WriteLevel0Table()` 将 imm 写入 sstable,并应用 log,更新 VersionSet,删除废弃文件
- `DBImpl::MaybeScheduleCompaction()`:**发起 compaction 调度**
- `DBImpl::BackgroundCompaction()`:负责 compaction 总体逻辑
- `DBImpl::DoCompactionWork(CompactionState*)`:通过读取提交的 `CompactionState*` 来 **完成实质性的 compaction**
- `DBImpl::InstallCompactionResults(CompactionState*)`:将 compact 过程中记录的操作(VersionEdit)生效,**加入 VersionSet**
- `DBImpl::FinishCompactionOutputFile(CompactionState*)`:输出文件之后的 finish 操作(包括**写入 filter-block,index-block 和 footer 等**,将新 sstable 加入 TableCache)
- `DBImpl::CleanupCompaction(CompactionState*)`:完成 compaction 之后的工作


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

### `DB::Open()` 调用层次

用户调用,启动 db,并准备环境

### `DestroyDB()` 调用层次

用户调用,删除 db


&nbsp;    


### `DBImpl::NewDB()` 调用层次

- `DB::Open()`:
  - `DBImpl::Recover()`:如果 dbname 不存在
      - `DBImpl::NewDB()`

### `DBImpl::DBImpl()` 调用层次

`DB::Open()` -> `DBImpl::DBImpl()`

### `DBImpl::~DBImpl()` 调用层次

用户调用 delete db,关闭 db


&nbsp;    

### `DBImpl::Get()` 调用层次

用户调用,查找给定 key 的 value

### `DBImpl::Write(WriteBatch*)` 调用层次

- `DB::Put()`
- `DB::Delete()`

### `DBImpl::CompactRange()` 调用层次

用户 **手动触发 compaction**

- `DBImpl::CompactRange()`
  - `Version::OverlapInLevel()`:首先查看**和这些 range 存在 overlap 的最底层的 level**
  - `DBImpl::TEST_CompactMemTable()`:等待 memtable 进行 compaction (不管是否存在 overlap)
  - 循环调用 `DBImpl::TEST_CompactRange()`:然后遍历这些 level,分别对每层进行 compact range
      - `DBImpl::MaybeScheduleCompaction()`
          - `DBImpl::BackgroundCompaction()`


&nbsp;   


### `DBImpl::Recover()` 调用层次

- `DB::Open()`:
  - `DBImpl::Recover()`:
      - `VersionSet::Recover()`:从 CURRENT 读 Manifest (VersionEdit)
      - 如果有 log 比 Manifest 文件中记录的 log 要新,那说明上次没来得及从内存 dump,于是从 log 回复数据,调用 `RecoverLogFile()`
      - `DBImpl::RecoverLogFile()`:打开指定的 log 文件,回放日志。期间可能会执行 compaction,产生新的 level-0 sstable 文件(`DBImpl::WriteLevel0Table(MemTable*, VersionEdit*, Version*)`),记录文件变动到 edit 中

### `DBImpl::RecoverLogFile()` 调用层次

- `DBImpl::Recover()`:如果需要从 log 回放数据
  - `DBImpl::RecoverLogFile()`:
      - `log::Reader::ReadRecord()`:读出具体信息
      - `WriteBatchInternal::SetContents()`:设置 WriteBatch
      - `WriteBatchInternal::InsertInto()`:将 WriteBatch 写入 memtable
      - 如果 mem 过大就 compact,调用 `DBImpl::WriteLevel0Table()`
      - 如果要继续用 mem 和 log,把资源给 mem_
      - 如果不继续用 mem,就调用 `DBImpl::WriteLevel0Table()` dump memtable


&nbsp;   


### `DBImpl::NewInternalIterator(SequenceNumber*)` 调用层次

`DBImpl::NewIterator()` 调用该函数生成 多路归并迭代器,用于生成 DBIter

### `DBImpl::BuildBatchGroup()` 调用层次

`DBImpl::Write(WriteBatch*)` -> `DBImpl::BuildBatchGroup()`

### `DBImpl::DeleteObsoleteFiles()` 调用层次

在 compaction 之后调用

- `DBImpl::CompactMemTable()`
- `DBImpl::BackgroundCompaction()`
- `DB::Open()`

### `DBImpl::MakeRoomForWrite()` 调用层次

- `DBImpl::CompactRange()`
  - `DBImpl::TEST_CompactMemTable()`
      - `DBImpl::Write(WriteBatch* = NULL)`:接下
  - `DBImpl::TEST_CompactRange()`

<a></a>

- `DB::Put()`,`DB::Delete()`
  - `DBImpl::Write(WriteBatch* != NULL)`:接下

<a></a>

- `DBImpl::Write(WriteBatch*)`
  - `DBImpl::MakeRoomForWrite(force = (WriteBatch* == NULL))`
      - `DBImpl::MaybeScheduleCompaction()`

### `DBImpl::WriteLevel0Table(MemTable*, VersionEdit*, Version*)` 调用层次

- `DB::Open()`
  - `DBImpl::Recover()`
      - `VersionSet::Recover()`:读 manifest
      - `DBImpl::RecoverLogFile()`:回放日志,可能调用 `DBImpl::WriteLevel0Table()`
            - `DBImpl::WriteLevel0Table()`:dump memtable

<a></a>

另一种见下图:`DBImpl::CompactMemTable()` -> `DBImpl::WriteLevel0Table()`

### `DBImpl::CompactMemTable()` 调用层次

`DBImpl::DoCompactionWork()` 中如果有 imm,就阻塞 **等待** `DBImpl::CompactMemTable()`

`DBImpl::BackgroundCompaction()` 中如果有 imm,就 `DBImpl::CompactMemTable()` 然后 **退出**

### `DBImpl::MaybeScheduleCompaction()` 调用层次

- 由 `DBImpl::TEST_CompactRange()` 调用:用户手动触发 compaction,compact 一个 range
- 由 `DBImpl::BackgroundCall()` 调用:刚刚的 compaction 有可能产生过多文件在一个 level,再次尝试 compaction
- 由 `DBImpl::MakeRoomForWrite()` 调用:新建 mem,dump imm
- 由 `DBImpl::Get()` 调用:如果是从 sstable 文件查询出来的,检查是否需要做 compaction
- 由 `DB::Open()` 调用:回放 log,删除废弃文件,进行 compaction

**这张图的实现细节在 [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf) 中最后 compact 部分有详细讲解。**

![](assets/LevelDB-BackgroundCompaction-Processes_11_12.jpg)

### `DBImpl::BackgroundCompaction()` 调用层次

`DBImpl::MaybeScheduleCompaction()` -> schedule `DBImpl::BackgroundCompaction()`

### `DBImpl::DoCompactionWork(CompactionState*)` 调用层次

`DBImpl::BackgroundCompaction()` -> `DBImpl::DoCompactionWork()`

### `DBImpl::InstallCompactionResults(CompactionState*)` 调用层次

`DBImpl::DoCompactionWork()` -> `DBImpl::InstallCompactionResults()`

### `DBImpl::FinishCompactionOutputFile(CompactionState*)` 调用层次

`DBImpl::DoCompactionWork()` -> `DBImpl::FinishCompactionOutputFile()`

### `DBImpl::CleanupCompaction(CompactionState*)` 调用层次

`DBImpl::BackgroundCompaction()` -> `DBImpl::CleanupCompaction()`


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

### `DB::Open()`

- `DBImpl::Recover()`
  - 检查 CURRENT
  - 调用 `VersionSet::Recover()`,从 CURRENT 读 Manifest (VersionEdit)
  - 检查有没有更新的 log,如果有 log 比 Manifest 文件中记录的 log 要新,那说明上次没来得及从内存 dump,于是从 log 回放数据,按照 log 大小顺序调用 `DBImpl::RecoverLogFile()`
  - 更新 VersionSet 的 max sequence
- 如果没有回放日志 或者 mem 没有继续使用(`DBImpl::RecoverLogFile()`),那么**生成 新-log 和 新-mem**
- 如果不打算继续用 manifest(`VersionSet::Recover(bool *save_manifest)`),就写入:`VersionSet::LogAndApply()`
- 删除废弃文件(`DBImpl::DeleteObsoleteFiles()`)
- 并尝试 compaction(`DBImpl::MaybeScheduleCompaction()`)

### `DestroyDB()`

用户调用,删除 db

- 获取 dbname 目录的文件列表到 filenames 中,如果为空则直接返回
- 锁文件 <dbname>/lock,如果锁失败就返回
- 遍历 filenames 文件列表,过滤掉 lock 文件,依次调用 `Env::DeleteFile()` 删除
- 释放 lock 文件,并删除之,然后删除文件夹


&nbsp;    


### `DBImpl::NewDB()`

建立一个 Manifest 文件,然后将这个版本的 Manifest 文件的文件名作为内容写入 CURRENT 文件

### `DBImpl::DBImpl()`

设置参数,生成 Memtable,TableCache 和 VersionSet

### `DBImpl::~DBImpl()`

用户调用 delete db,关闭 db

- 等待后台 compaction 任务结束
- 释放 db 文件锁, <dbname>/lock 文件
- 删除 VersionSet 对象,并释放 MemTable 对象
- 删除 log 相关以及 TableCache 对象
- 删除 options 的 block_cache 以及 info_log 对象


&nbsp;    


### `DBImpl::Get()`

- 查 mem
- 查 imm
- 查 sstable,并尝试 compaction

### `DBImpl::Put()`

将用户 k-v 包装成 WriteBatch,调用 `DBImpl::Write(WriteBatch*)`

### `DBImpl::Delete()`

将用户 k-v 包装成 WriteBatch,调用 `DBImpl::Write(WriteBatch*)`

### `DBImpl::Write(WriteBatch*)`

- 将 `WriteBatch*` 收集进入 `writers_: std::deque<DBImpl::Writer*>`
- 调用 `DBImpl::BuildBatchGroup()` 整合一批 WriteBatch 为一个 `update: WriteBatch`
- 设置 sequece 为 last_sequence + 1
- **写入 log**:`log::Writer::AddRecord()`
- **写入 memtable**:`WriteBatchInternal::InsertInto()`
- 更新 last_sequence += 这个 `update` 的 count(`WriteBatchInternal::Count()`)
- 将 `update` 中的这一部分 WriteBatch 从 `writers_: std::deque<DBImpl::Writer*>` 中去掉

> sequence number 在写入 memtable 时会自增。   
> 具体参考 `WriteBatchInternal::InsertInto()` -> `WriteBatch::Iterate()`

### `DBImpl::NewIterator()`

- 调用 `DBImpl::NewInternalIterator()` 生成 `internal_iter`
- 再由 `NewDBIterator()` 包装成 遍历 db 的迭代器

### `DBImpl::CompactRange()`

- `Version::OverlapInLevel()`:首先查看**和这些 range 存在 overlap 的最底层的 level**
- `DBImpl::TEST_CompactMemTable()`:等待 memtable 进行 compaction (不管是否存在 overlap)
- 循环调用 `DBImpl::TEST_CompactRange()`:然后遍历这些 level,分别对每层进行 compact range,即调用 `DBImpl::MaybeScheduleCompaction()`


&nbsp;   


### `DBImpl::Recover()`

- 创建目录
- 检查 CURRENT
- 调用 `VersionSet::Recover()`,从 CURRENT 读 Manifest (VersionEdit)
- 检查有没有更新的 log,如果有 log 比 Manifest 文件中记录的 log 要新,那说明上次没来得及从内存 dump,于是**从 log 回放数据**,按照 **log 大小顺序**调用 `DBImpl::RecoverLogFile()`
- 更新 VersionSet 的 max sequence

### `DBImpl::RecoverLogFile()`

打开指定的 log 文件,回放日志。期间可能会执行 compaction,产生新的 level-0-sstable 文件,记录文件变动到 edit 中

- `log::Reader::ReadRecord()`:读出具体信息
- `WriteBatchInternal::SetContents()`:设置 WriteBatch
- `WriteBatchInternal::InsertInto()`:将 WriteBatch 写入 memtable
- 如果 mem 过大就 compact,调用 `DBImpl::WriteLevel0Table()`
- 如果要继续用 mem 和 log,把资源给 mem_
- 如果不继续用 mem,就调用 `DBImpl::WriteLevel0Table()` dump memtable

> `DBImpl::RecoverLogFile()`:  
> &emsp;&emsp;&emsp;&emsp; `log_number` -> `LogFileName` -> `SequentialFile` -> `log::Reader` -> `record (Slice)` -> `WriteBatch` -> `MemTable`->   
> &emsp;&emsp;&emsp;&emsp; 对于最后一个 log,&emsp;(1) 继续用 `mem` 和 log    
> &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;(2) 不继续用,就 cmpact,`WriteLevel0Table()` 并且信息存入 VersionEdit


&nbsp;   


### `DBImpl::NewInternalIterator(SequenceNumber*)`

收集当前版本所有可用 iterator,产生一个 merging iterator

- **mem** iterator
- **imm**(如果有) iterator
- 当前 version 的 iterator(遍历 **sstable**,即 **TwoLevelIterator**)

然后调用 `MergingIterator()` 返回一个 **多路归并迭代器**

> 这个参数 `SequenceNumber*` 一开始让我感到诧异,因为函数只是从 `VersionSet::LastSequence()` 获取一个最新的 sequence number 给这个指针,其他啥都没做。   
> 然后这个 sequence 在 `DBImpl::NewIterator()` 紧接着传给 `NewDBIterator()`,**那为啥不在 `DBImpl::NewIterator()` 调用 `NewDBIterator()` 的时候直接给 `VersionSet::LastSequence()` 呢?**   
> 观察了一下想通了:原因在于 `DBImpl::NewIterator()` **没有上锁**,获得的 sequence number 可能和 `DBImpl::NewInternalIterator(SequenceNumber*)` 内部收集的 merging iterator 不一致。

### `DBImpl::BuildBatchGroup()`

`DBImpl::Write(WriteBatch*)` 调用 `DBImpl::BuildBatchGroup()`,将一部分 WriteBatch 拼接起来(一部分是因为控制 size),拼接的这一部分之后会在 `DBImpl::Write(WriteBatch*)` 中去掉。(`if(ready != last_writer) writers_.pop_front();`)

### `DBImpl::DeleteObsoleteFiles()`

保护正在进行的 compaction 文件,删除过期文件。(如果是 table 文件,调用 `TableCache::Evict()` 清除缓存)

> 下面抄自 [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)

db 中当前 Version 的 sstable 均在 `VersionSet::current_` 中,并发的读写造成会有多个 Version 共存,`VersionSet::dummy_versions_` 中有包含 `VersionSet::current_` 所有正在服务的 Version。凡是正在服务的 Version 中的 sstable 文件都认为是 live 的。 `DeleteObsoleteFiles()` 删除 非-live 的 sstable 以及其他类型的废弃文件。

- 取得 `pending_output_` 中仍存在的以及 live 的 sstable 文件(`VersinoSet::AddLiveFiles()`,遍历 `VersionSet::dummy_versions_` 即可) ,作为所有 live 的文件
- 遍历 db 目录下所有的文件名,删除非 live 的 sstable 以及废弃的其他类型文件
  - log 文件保留大于 `VersionSet::log_number_` 以及辅助 log 文件
(`VersionSet::prev_log_number_`)
  - Manifest 文件只保留当前的
  - sstable 文件以及临时文件(repair 时会产生)只保留 live 的
  - CURRENT/LOG/LOCK 文件均保留

### `DBImpl::MakeRoomForWrite()`

[Compactions - Timing](https://github.com/google/leveldb/blob/master/doc/impl.md#timing) - Solution 2:**当 level-0 文件较多时,控制写入速率。**

> 下面这段抄自 [DBImpl - MakeRoomForWrite](https://dirtysalt.github.io/html/leveldb.html#org6a4ae1d)

- 如果允许 delay 的话并且 level-0 文件数目超过 slowdown 的阈值的话,那么就会先尝试 delay 1s,下次不会进行 delay
- 如果不是 force 的话并且 memtable 空间允许的话那么直接返回
- 剩下的逻辑就是 force 出一个 memtable 了,那么这个时候必须进行 compaction to level-0
- 先检查是否正在被 memtable compaction,如果正在的话那么等待
- 然后查看 level-0 文件数目是否过多,如果过多的话那么也等待(**限制写入速度保证系统 balance**)
- 最后创建新的 memtable 以及 logfile,将原来的 memtable 保存起来准备后台 compaction
- 发起 compaction(`DBImpl::MaybeScheduleCompaction()`),并且 `force = false`

按照性能角度出发的话,这种逻辑应该非常 make sense

### `DBImpl::WriteLevel0Table(MemTable*, VersionEdit*, Version*)`

**Dump Memtable**,不一定在 level-0,version 的修改记录在 `VersionEdit*` 里面

- 获取 memtable 的 iterator
- 将 iter 送入 `BuildTable()` 写入文件
- 文件信息存在 `FileMetaData`
- 调用 `Version::PickLevelForMemTableOutput(smallest, largest)` **找到尽可能低的 level 作为这个 sstable 的 level**
- 将 level 和 `FileMetaData` 信息存入 `VersionEdit*`(调用 `VersionEdit::AddFile()`)

### `DBImpl::CompactMemTable()`

- 调用 `DBImpl::WriteLevel0Table(imm_, &version_edit, version_base)`
- 更新当前的 log_number,应用 VersionEdit,生效新的 Version(`VersionSet::LogAndApply()` 应用 VersionEdit,写入 manifest 文件)
- 删除废弃文件(`DBImpl::DeleteObsoleteFiles()`)

### `DBImpl::MaybeScheduleCompaction()`

- 如果正在 compact,那么返回
- 如果 db 正在退出,那么返回
- 检查是否需要 compaction:如果都没有,那么返回
  - imm 为空(不需要 dump memtable)
  - 手动 compact 未设置
  - `VersionSet::NeedsCompaction()`,是否需要 自动触发 compact
- 主线程调度 `DBImpl::BackgroundCall()` 加入队列,然后返回

**注**:`DBImpl::BackgroundCall()` 最后会**再次调用** `DBImpl::MaybeScheduleCompaction()`

- `BackgroundCall()` 调用 `BackgroundCompaction()`
- 之后再次调用 `MaybeScheduleCompaction()`,目的是:新的 Version 中可能 level 中有过多的 sstable,所以再次尝试 compaction
- 主线程将任务入队列即返回,不会有递归栈溢出的问题

### `DBImpl::BackgroundCompaction()`

负责 compaction 总体逻辑

- 如果有 imm,立即 `DBImpl::CompactMemTable()` 然后退出
- 准备 compaction 的信息:
  - 如果是 **手动** compaction,调用 `VersionSet::CompactRange()`
  - 如果是 **自动触发** compaction,调用 `VersionSet::PickCompaction()`
- 处理 compaction:
  - **自动触发** compaction:
      - `VersionEdit::DeleteFile(level-n)`
      - `VersionEdit::AddFile(level-n+1)`
      - `VersionSet::LogAndApply(VersionEdit*)`
  - **手动** compaction:
      - `DBImpl::DoCompactionWork()`
      - `DBImpl::CleanupCompaction()`
      - `DBImpl::DeleteObsoleteFiles()`
- 如果是 手动触发,最后整理一下 range 的信息,然后把 手动触发 设为 NULL

### `DBImpl::DoCompactionWork(CompactionState*)`

**实际的 compact 过程就是对多个已经排序的 sstable 做一次 merge 排序,丢弃掉相同 key 以及删除的数据。**

- 调用 `VersionSet::MakeInputIterator(Compaction*)` 获得遍历 compaction 文件的 iterator
- 遍历-iterator
  - 检查一下是否应该停止或等待:
      - 如果有 imm 就阻塞(调用 `DBImpl::CompactMemTable()`)
      - 当前 sstable 与 grandparent 的 overlap 是否超过阈值(`Compaction::ShouldStopBefore(internal_key)`);如果是,调用 `DBImpl::FinishCompactionOutputFile()` 写完后立即退出 遍历-iterator
  - 判断 本-key 是否应该 drop
      - 如果是 新-key,就赋值给 cur_key(当前-key,有可能出现多个同值的 key)
      - 判断 当前-key(不是 本-key)的最后一个 sequence(不是 本-key 的 sequence) 是否小于快照。若小于(一定不是第一个出现的 新-key),则 drop;因为第一个 新-key 的 sequence 是 max,绝不会被 drop(**这一条好像是 drop 重复的 key,但我没看懂如果 本-key 的 sequence 比快照大那不就不 drop 了???**)
      - 若以下均成立,则 drop
          - 本-key 是 *删除操作*(`kTypeDeletion`)
          - 本-key 的 sequence 小于快照
          - 对于要将 本-key 写入 level-(n+1),是否 level-(n+1) 以上的 level 不存在这个 key
  - 处理没有被 drop 的 k-v:
      - (第一次到这里)生成 sstable(`DBImpl::OpenCompactionOutputFile(CompactionState*)`)
      - `TableBuilder::Add()` **加入 k-v**
      - 如果 sstable 太大了,就 `DBImpl::FinishCompactionOutputFile()` 结束 sstable 的创建
- 调用 `DBImpl::FinishCompactionOutputFile()` **结束 sstable 的创建**
- 更新 compact 的统计信息
- 调用 `DBImpl:: InstallCompactionResults()` **更新 VersionSet,并写入 manifest 文件**

> 为啥总是把 iterator 的 ++ 放在 for 循环体代码块的最后,而不是 for 后面括号里。。。第二次看见这样子,,有啥用??

### `DBImpl::InstallCompactionResults(CompactionState*)`

- 删除 input 的两层 level:`Compaction::AddInputDeletions(VersionEdit* edit)` -> `VersionEdit::DeleteFile()`
- 加入 output 的 level+1 的 file:`VersionEdit::AddFile()`
- 更新 version,并写入 manifest 文件:`VersionSet::LogAndApply(VersionEdit*)`

### `DBImpl::FinishCompactionOutputFile(CompactionState*)`

- `TableBuilder::Finish()`:写入 filter-block,meta-block 和 footer 等
- 记录统计信息
- sync,close 文件
- 新生成的 sstable 文件加入 TableCache(调用 `TableCache::NewIterator(file_number)`)

### `DBImpl::CleanupCompaction(CompactionState*)`

- 如果上一次最后一个 sstable 未完成就异常结束,修复状态(`TableBuilder:: Abandon()`)
- 将已经成功完成的 sstable `FileNumber` 从 `pending_outputs_` 中去除


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb - handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb - DBImpl](https://dirtysalt.github.io/html/leveldb.html#org6a4ae1d) 参考了不少
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf) 在 `DBImpl::xxxCompaction()` 方面讲得很清楚
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html) 很详细
- [庖丁解LevelDB之概览](http://catkang.github.io/2017/01/07/leveldb-summary.html)

================================================
FILE: architecture/DB/DBIter - 2018-11-14 - rsy.md
================================================
# DBIter - 2018-11-14 - rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/db_iter.cc`, `db/db_iter.h`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

封装 db 迭代器


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

`Iterator* NewDBIterator(DBImpl*, Iterator*, SequenceNumber)`


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

- `DBImpl::NewIterator()`
  - 调用 `DBImpl::NewInternalIterator(SequenceNumber*)` 生成 `internal_iter`
  - 调用 `NewDBIterator(internal_iter)` 生成遍历 db 的迭代器

&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

- `DBIter::Next()`:
- `DBIter::Prev()`:
- `DBIter::Seek(const Slice& target)`:
- `DBIter::SeekToFirst()`:
- `DBIter::SeekToLast()`:

这个地方挺奇怪,大致意思就是有很多 key 重复,要跳过。   
不过实现没太搞懂。。。

- `DBIter::FindNextUserEntry(bool skipping, std::string* skip)`:
- `DBIter::FindPrevUserEntry()`:


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb - handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb](https://dirtysalt.github.io/html/leveldb.html)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)

================================================
FILE: architecture/DB/Log - 2018-11-08 - rsy.md
================================================
# Log - 2018-11-08 - rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/log_format.h`, `db/log_reader.h`, `db/log_reader.cc`, `db/og_writer.h`, `db/log_writer.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](./assets/LOG_Reader_UML_11_09.png)

![](./assets/LOG_Writer_UML_11_09.png)

### [leveldb Log format](https://github.com/rsy56640/leveldb/blob/master/doc/log_format.md)

    block := record* trailer?
    record :=
      checksum: uint32     // crc32c of type and data[] ; little-endian
      length: uint16       // little-endian
      type: uint8          // FULL, FIRST, MIDDLE, LAST
      data: uint8[length]

![](./assets/log_structure_11_09.jpeg)

![](./assets/log_block_record_structure_11_09.png)

> 2018-11-14 看 `DBImpl::Write()` 回来看这个,感觉这个图好像不太符合 `log::Writer::AddRecord()` 中的实现。。。之后再研究

&nbsp;   
<a id="module_function"></a>
## 模块功能

`DBImpl::Write()` 中先写 log,然后更新 memtable。   
所有的写操作都必须先成功的 append 到操作日志中,然后再更新内存 memtable。这样做有两点:

- 可以将随机的写 IO 变成 append,极大的提高写磁盘速度
- 防止在节点 down 机导致内存数据丢失,造成数据丢失,这对系统来说是个灾难。

如果遇到重启等情况,LevelDB就会将已经持久化的文件与日志进行一个merge操作,这就是LSM, 与HBase的恢复操作很相似。

![](./assets/two_log_11_09.jpeg)

![](./assets/log_write_11_09.jpeg)

**内存对象 => 二进制数组(Slice对象) => leveldb::log 切割成小块并打上 hash(crc32) => 写入硬盘**

![](./assets/log_read_11_09.jpeg)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `log::Writer::AddRecord()`:分块block,调用 `EmitPhysicalRecord()`
- `log::Writer::EmitPhysicalRecord()`:调整格式,计算crc校验,并写入数据到文件(调用了 `Flush()`)
- `log::Reader::ReadRecord()`:读出下一个整个 `record`,如果不行,返回 false,`scratch` 是中间临时存储(因为有些 `record` 一次读不完)
- `log::Reader::SkipToInitialBlock()`:跳到要读的第一个 `record`
- `log::Reader::ReadPhysicalRecord()`:读文件,检验,结果放到 `backing_store_`


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

- `log::Reader::ReadRecord()` 在 `DBImpl::RecoverLogFile()` 和 `VersionSet::Recover()` 中调用,方式如下:

        while (reader.ReadRecord(&record, &scratch) && status.ok())

即持续地读,中间结果存储在 `scratch` 中,最后扔给 `record`。

-----

log 的生成与删除:

- `DBImpl::Write()` 先调用 `log::Writer::AddRecord()` 写入 log,然后写入 memtable
- memtable 真正写盘时,将它对应的日志文件从磁盘上删掉。
  - `DBImpl::DeleteObsoleteFiles()`

> `DBImpl::WriteLevel0Table()`之后就表示 `imm_`已经被写入磁盘了。此时它所对应的日志文件就不再需要了。但是怎么找到它所对应的日志文件呢?从之前 `DBImpl::MakeRoomForWrite()` 中的代码中我们知道,`logfile_number_` 是表示指向当前 `mem_` 的 `log_` 文件号。而指向当前这个被写入到磁盘的 `imm_` 的日志文件号肯定比 `logfile_number_` 小。进入 `DBImpl::DeleteObsoleteFiles()` 函数我们就可以看到该函数将所有过期日志文件都删掉,过期日志文件的判断标准就是:

        case kLogFile:
          keep = ((number >= versions_->LogNumber()) ||
                  (number == versions_->PrevLogNumber()));
          break;


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`log::Writer::AddRecord()`: 接受一整个 WriteBatch 的内容(来源于 `DBImpl::Write()`),然后不断地拼成 block


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [levelDB源码分析-Log文件](https://blog.csdn.net/tankles/article/details/7663873)
- [跟吉姆一起读LevelDB 4.数据库恢复(1)](https://zhuanlan.zhihu.com/p/27400189)
- [跟吉姆一起读LevelDB 5.数据库恢复(2)](https://zhuanlan.zhihu.com/p/27417009)
- [跟吉姆一起读LevelDB 6.数据库恢复(3)](https://zhuanlan.zhihu.com/p/27467584)
- [levelDB - log](https://dirtysalt.github.io/html/leveldb.html#org2f37695)
- [leveldb源码分析之读写log文件](http://luodw.cc/2015/10/18/leveldb-08/)
- [log日志文件-leveldb源码剖析(4)](http://www.pandademo.com/2016/03/log-leveldb-source-dissect-4/)
- [LevelDB源码解析12.读取Record](https://zhuanlan.zhihu.com/p/44150093)
- [LevelDB源码分析之十六:.log文件](https://blog.csdn.net/caoshangpa/article/details/79098716)
- [LevelDB源码分析之十:LOG文件](https://blog.csdn.net/caoshangpa/article/details/78925719)
- [leveldb源码剖析---日志系统](https://blog.csdn.net/Swartz2015/article/details/69228711)
- [leveldb源码分析--日志](https://www.cnblogs.com/KevinT/p/3815522.html)
- [levelDB Log-writer](https://www.cnblogs.com/shenzhaohai1989/p/3905354.html)
- [LevelDB源码剖析之Env与log::Writer](http://mingxinglai.com/cn/2013/01/leveldb-log-and-env/)
- [leveldb源码学习--log](https://www.jianshu.com/p/524175e9f34d)

================================================
FILE: architecture/DB/Manifest & VersionEdit - 2018-11-07 - rsy.md
================================================
# Manifest & VersionEdit - 2018-11-07 - rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db.version_set.cc`, `db/version_edit.h`, `db/version_edit.cc`

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](./assets/VersionEdit_UML_11_08.png)

![](./assets/Version_VersionEdit_11_08.png)

**每一次 compact 都会改变当前 Version,VersionEdit 记录了 Version 的增量。通过 VersionSet::Builder 将 VersionEdit 融进 Version。**

![](./assets/levelDB_recover_control.png)

![](./assets/version_recover_10_12.jpeg)

Manifest:
> A MANIFEST file lists the set of sorted tables that make up each level, the corresponding key ranges, and other important metadata. A new MANIFEST file (with a new number embedded in the file name) is created whenever the database is reopened. The MANIFEST file is formatted as a log, and changes made to the serving state (as files are added or removed) are appended to this log.

为了避免进程崩溃或机器宕机导致的数据丢失,LevelDB 需要将元信息数据持久化到磁盘,承担这个任务的就是 Manifest 文件。可以看出每当有新的 Version 产生都需要更新 Manifest,很自然的发现这个新增数据正好对应于 VersionEdit 内容,也就是说 Manifest 文件记录的是一组 VersionEdit 值,在 Manifest 中的一次增量内容称作一个 Block,其内容如下:**就是 VersionEdit 的内容**。

Current:
> CURRENT is a simple text file that contains the name of the latest MANIFEST file.


&nbsp;   
<a id="module_function"></a>
## 模块功能

为了重启 db 后可以恢复退出前的状态,需要将 db 中的状态保存下来,这些状态信息就保存在 manifest 文件中。  
当 db 出现异常时, 为了能够尽可能多的恢复,manifest 中不会只保存当前的状态,而是将历史的状态都保存下来。 又考虑到每次状态的完全保存需要的空间和耗费的时间会较多,当前采用的方式是,只在 manifest 开始保存完整的状态信息(`VersionSet::WriteSnapshot()`),接下来只保存每次
compact 产生的操作(VesrionEdit),重启 db 时,根据开头的起始状态,依次将后续的 VersionEdit replay,即可恢复到退出前的状态(Vesrion)。

将 VersionEdit 加入 Version 时要用到帮助类`class VersionSet::Builder` 中 `Builder::Apply(VersionEdit*)`,使用 `Builder::SaveTo(Version* v)` 把这些version的叠加总和存进去。


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

**Version 是全量,VersionEdit 是增量**,VersionEdit 中记录了要删除和增加的文件,`pair<int, FileMetaData / uint64_t>` 中 `int` 表示 level。

- `VersionSet::Builder::Apply(VersionEdit*)`:**加入增量**。在 `VersionSet::LogAndApply()` 和 `VersionSet::Recover()` 中调用。
- `VersionSet::Builder::SaveTo(Version*)`:**将结果存入 空Version\*:把 上一个Version 和 Versionedit 中的文件 按顺序 调用MaybeAddFile()**。在 `VersionSet::LogAndApply()` 和 `VersionSet::Recover()` 中调用。
- `VersionSet::Builder::MaybeAddFile(Version*, int level, FileMetaData*)`:**如果不删除 `FileMetaData*`,就加入相应 level**。在前面的 `VersionSet::Builder::SaveTo(Version*)` 中调用。


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

Manifest 文件:

- 在 `VersionSet::LogAndApply()` 中被刷新(写入)
- 在 `DB::Open()` -> `DBImpl::Recover()` -> `VersionSet::Recover()` 中被读取:
  - 先读 `CURRENT` 文件,其指向当前 Manifest 文件
  - 然后 `log::Reader` 读文件,扔给 `VersionEdit::DecodeFrom()` 读出 `VersionEdit` 即 Manifest

> 话说是不是有个 `status` 处理有啥问题,我把 Manifest 删了,直接 **段错误**???   
> 代码里面是 `return Status::Corruption("CURRENT points to a non-existent file", s.ToString());`


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

**我觉得有些实现不如之前数据序列化那里优秀。**   
比如:

- `VersionSet::Builder::Apply()`:
  - for循环中调用size(),solution: 提前放到栈上。
  - x 如果大于 100,x就赋值为 100,solution:不用 if,**不破坏流水线**

<a></a>

    int map[2] = { 100, x };  
    x = map[(x - 100) >> 31];


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb源码分析之version、version_edit和version_set](http://luodw.cc/2015/10/31/leveldb-16/)
- [LevelDB源码解析14. Version的数据结构](https://zhuanlan.zhihu.com/p/44584617)
- [leveldb源码分析之version、version_edit和version_set](http://luodw.cc/2015/10/31/leveldb-16/)
- [版本控制VersionEdit-leveldb源码剖析(13)](http://www.pandademo.com/2016/05/version-control-versionedit-leveldb-source-dissect-13/)
- [庖丁解LevelDB之版本控制](http://catkang.github.io/2017/02/03/leveldb-version.html)

================================================
FILE: architecture/DB/Memtable - 2018-10-04 - rsy.md
================================================
# Memtable - 2018-10-04 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/memtable.h`, `db/memtable.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

在 写 level-0 时将 `iter` 传入 `BuildTable` 中写入文件。

在 leveldb 中,所有内存中的 k-v 数据都存储在 `memtable` 中,物理 disk 则存储在 `SSTable` 中。在系统运行过程中,如果 `memtable` 中的数据占用内存到达指定值(`Options.write_buffer_size`),则 Leveldb 就自动将 `memtable` 转换为 `immutable memtable`,并自动生成新的 `memtable`,也就是 Copy-On-Write 机制了。
`immutable memtable` 则被新的线程 Dump 到磁盘中, Dump 结束则该 `immutable memtable` 就可以释放了。(所以,同时最多会存在两个  `memtable`)


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/Memtable_UML_10_04.png)

![](assets/memtable_10_07.jpg)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `Add()`:提供 **插入**、**删除**(只记录删除操作,而不真正删除,直到 Compaction)
- `Get()`:查找
- `NewIterator()`:封装了 memtable 的迭代器,用于
  - `DBImpl::WriteLevel0Table()` 调用 `BuildTable()` 写入文件
  - 给用户提供 iterator(`DBImpl::NewInternalIterator()`)


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

**从 WriteBatch 写入**:

- `DBImpl::Write(WriteBatch*)`
- `WriteBatchInternal::InsertInto(const WriteBatch*, MemTable*)`
- `WriteBatch::Iterate(MemTableInserter*)`:**将 k-v 不断迭代地插入 memtable**
- `MemTableInserter::Put()` 和 `MemTableInserter::Delete()` 转发给 `Memtable::Add()`

**Dump**:`DBImpl::WriteLevel0Table()` 在 写 level-0(不一定) 时将 `iter` 传入 `BuildTable()` 中写入文件。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

内存数据库中,key称为internalKey,其由三部分组成:

- 用户定义的key:这个key值也就是原生的key值
- 序列号:leveldb中,每一次写操作都有一个sequence number,标志着写入操作的先后顺序。由于在leveldb中,可能会有多条相同key的数据项同时存储在数据库中,因此需要有一个序列号来标识这些数据项的新旧情况。序列号最大的数据项为最新值
- 类型:标志本条数据项的类型,为更新还是删除

![](assets/memtable_internalKey_10_04.png)

排序基于 user key 的 sequence number,其排序比较依据依次是:

- 首先根据 user key 按升序排列
- 然后根据 sequence number 按降序排列
- 最后根据 value type 按降序排列(这个其实无关紧要)


>讲道理 `memtable` 应该给一个工厂接口的,毕竟dtor是私有的,不过写自动存储期不能编译就是了。

&nbsp;   
KV 的实际存储格式:

![](assets/KV_format_10_04.png)

- `Add()`:把 `key + ValueType + value` 序列化之后塞进去
- `Get()`:反序列化,找 key
- `NewIterator()`:memtable 对 key 的查找和遍历封装成 `MemTableIterator`。 底层直接使用 SkipList 的类 Iterator 接口
- `MemTableIterator`:用于解析格式、遍历


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb MemTable](https://dirtysalt.github.io/html/leveldb.html#org2c88c7b)
- [leveldb MemTable](https://dirtysalt.github.io/html/leveldb.html#orgf9c3f07)
- [leveldb-handlebook 内存数据库](https://leveldb-handbook.readthedocs.io/zh/latest/memorydb.html#id9)
- [leveldb源码分析之memtable](http://luodw.cc/2015/10/17/leveldb-06/)
- [MemTable与SkipList-leveldb源码剖析(3)](http://www.pandademo.com/2016/03/memtable-and-skiplist-leveldb-source-dissect-3/)
- [LevelDB源码分析之八:memtable](https://blog.csdn.net/caoshangpa/article/details/78901792)
- [LevelDB源码剖析之MemTable](http://mingxinglai.com/cn/2013/01/leveldb-memtable/)
- [leveldb memtable](https://www.cnblogs.com/shenzhaohai1989/p/3904166.html)
- [leveldb源码分析--Memtable](https://www.cnblogs.com/KevinT/p/3814012.html)

================================================
FILE: architecture/DB/README.md
================================================
- [Status](#status)
- [KeyFormat](#key_format)
- [WriteBatch](#write_batch)
- [TableCache](#table_cache)
- [Memtable](#memtable)
- [Builder](#build_table)
- [ValueType & SequenceNumber](#valuetype_sequence)
- [Manifest & VersionEdit](#manifest_versionedit)
- [Recover](#recover)
- [Log](#log)
- [Version & VersionSet](#version_versionset)
- [DBImpl](#dbimpl)


&nbsp;   
<a id="status"></a>
## [Status](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Status-2018-10-02-dz.md)

Status 类封装对 db 操作返回的结果,除 success,都可以包含错误码和两条错误信息。


&nbsp;   
<a id="key_format"></a>
## [KeyFormat](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/dbformat_key-2018-10-01-ss.md)

![](assets/Key_format_10_04.png)   
![](assets/KV_format2_10_04.png)   


&nbsp;   
<a id="write_batch"></a>
## [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md)

leveldb 内部的一个批量写的结构,在 leveldb 为了提高插入和删除的效率,在其插入过程中都采用了批量集合相邻的多个具有相同同步设置的写请求以批量的方式进行写入。

![](assets/WriteBatch_rep_content_10_01.png)


&nbsp;   
<a id="table_cache"></a>
## [TableCache](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/TableCache%20-%202018-09-30%20-%20rsy.md)

`TableCache` 缓存 `Table` 对象,每个 DB 一个。

`TableCache` 的 k-v 格式:

- 以 `file_number` 作 key
- 以 `TableAndFile` 对应的预加载索引作为 value

table cache 缓存的是 sstable 的索引数据

简单梳理下到 `TableCache` 的查找流程

- 用户提交 key 查询,交由 `Status DBImpl::Get()` ,获取两种 `MemTable` 和当前 `Version`
- 依次查询 `memtable`、`immutable memtable`
- 未找到则在当前 `Version`上 `Status Version::Get()`,依次从最低 level 到最高 level 查询直至查到
- 在每层确定可能包含该 key 的 SSTable 文件后,就会在所属 `VersionSet` 的 `table_cache` 中继续查询,即调用 `Status TableCache::Get()`

另外,`TableCache` entry 的插入在 Compaction 时也有体现,每当通过 Compatction 生成新的 SSTable 文件,也会以验证正常可用的方式更新该 SSTable 的索引信息到 `TableCache`。


&nbsp;   
<a id="memtable"></a>
## [Memtable](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Memtable%20-%202018-10-04%20-%20rsy.md)

k-v 的实际存储格式:

![](assets/KV_format_10_04.png)

在 写 level-0 时将 `iter` 传入 `BuildTable` 中写入文件。   
在 leveldb 中,所有内存中的 k-v 数据都存储在 `memtable` 中,物理 disk 则存储在 `SSTable` 中。在系统运行过程中,如果 `memtable` 中的数据占用内存到达指定值(`Options.write_buffer_size`),则 leveldb 就自动将 `memtable` 转换为 `immutable memtable`,并自动生成新的 `memtable`,也就是 Copy-On-Write 机制了。
`immutable memtable` 则被新的线程 Dump 到磁盘中, Dump 结束则该 `immutable memtable` 就可以释放了。(所以,同时最多会存在两个  `memtable`)

![](assets/memtable_10_07.jpg)


&nbsp;   
<a id="build_table"></a>
## [Builder](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Builder%20-%202018-10-03%20-%20rsy.md)

为 level-? 创建 sstable,之后选择合适的 level(`DBImpl::WriteLevel0Table()` -> `Version::PickLevelForMemTableOutput()`)


&nbsp;   
<a id="valuetype_sequence"></a>
## [ValueType & SequenceNumber](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/ValueType%20%26%20SequenceNumber%202018-11-07%20-%20rsy.md)

- `ValueType`:顺序写(sequential writing)的策略使得数据不可被更改,于是标记 “delete” 操作。占 **8** bits。在顶层调用 `Put()` 或 `Delete()` 后转发给 [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md),在每一个 K-V `record` 中存储
- `SequenceNumber`:标识每一次更新的版本。占 **56** bits。在 [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md) 中记录(为一批 `record`)

[WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md) 在 `InsertInto()` 中写入 memtable,`InsertInto()` 先拿出来 sequence number,然后中调用 `Iterate()` 不断迭代地将 batch 中的数据写入(每次调用 `Memtable::Add()`,**sequence 自增**)


&nbsp;   
<a id="manifest_versionedit"></a>
## [Manifest & VersionEdit](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Manifest%20%26%20VersionEdit%20-%202018-11-07%20-%20rsy.md)

**每一次 compact 都会改变当前 Version,VersionEdit 记录了 Version 的增量。通过 `VersionSet::Builder` 将 VersionEdit 融进 Version。**

![](./assets/Version_VersionEdit_11_08.png)

Manifest:
> A MANIFEST file lists the set of sorted tables that make up each level, the corresponding key ranges, and other important metadata. A new MANIFEST file (with a new number embedded in the file name) is created whenever the database is reopened. The MANIFEST file is formatted as a log, and changes made to the serving state (as files are added or removed) are appended to this log.

为了避免进程崩溃或机器宕机导致的数据丢失,LevelDB 需要将元信息数据持久化到磁盘,承担这个任务的就是 Manifest 文件。可以看出每当有新的 Version 产生都需要更新 Manifest,很自然的发现这个新增数据正好对应于 VersionEdit 内容,也就是说 Manifest 文件记录的是一组 VersionEdit 值,在 Manifest 中的一次增量内容称作一个 Block,其内容如下:**就是 VersionEdit 的内容**。

Current:
> CURRENT is a simple text file that contains the name of the latest MANIFEST file.


&nbsp;   
<a id="recover"></a>
## [Recover](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Recover%20-%202018-11-08%20-%20rsy.md)

![](./assets/version_recover_10_12.jpeg)

数据库每次启动时,都会有一个 recover 的过程,简要地来说,就是利用 Manifest 信息重新构建一个最新的 version


&nbsp;   
<a id="log"></a>
## [Log](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Log%20-%202018-11-08%20-%20rsy.md)

`DBImpl::Write()` 中先写 log,然后更新 memtable。   
所有的写操作都必须先成功的 append 到操作日志中,然后再更新内存 memtable。这样做有两点好处:

- 可以将随机的写 IO 变成 append,极大的提高写磁盘速度
- 防止在节点 down 机导致内存数据丢失,造成数据丢失,这对系统来说是个灾难。

如果遇到重启等情况,levelDB 就会将已经持久化的文件与日志进行一个 merge 操作

![](./assets/two_log_11_09.jpeg)

![](./assets/log_write_11_09.jpeg)

**内存对象 => 二进制数组(Slice对象) => leveldb::log 切割成小块并打上 hash(crc32) => 写入硬盘**

![](./assets/log_read_11_09.jpeg)


&nbsp;   
<a id="version_versionset"></a>
## [Version & VersionSet](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Version%20%26%20VersionSet%20-%202018-11-12%20-%20rsy.md)

保留旧的数据,是为了支持旧版本的查询操作(compact 时发生读操作;不会发生旧版本的写,因为 sequential-writing),于是自然地引入了版本控制的概念。

leveldb 对单版本的 sstable 文件管理,主要集中在 Version 类中。**Version 不会修改其管理的 sstable 文件,只有读取操作**。

将每次 compact 后的最新数据状态定义为 Version,也就是当前 db 元信息以及每个 level 上具有最新数据状态的 sstable 集合。 compact 会在某个 level 上新加入或者删除一些 sstable,但可能这个时候,那些要删除的 sstable 正在被读,为了处理这样的读写竞争情况,基于 sstable 文件一旦生成就不会改动的特点,每个 Version 加入引用计数,读以及解除读操作会将引用计数相应加减一。 这样, db 中可能有多个 Version 同时存在(提供服务) ,它们通过链表链接起来。当 Version 的引用计数为 0 并且不是当前最新的 Version 时,它会从链表中移除, 对应的, 该 Version 内的 sstable 就可以删除了(这些废弃的 sstable 会在下一次 compact 完成时被清理掉)。

![](./assets/Version_VersionEdit_11_08.png)

### Version 版本类

这个类主要功能

- 首先是提供了在**当前版本搜索键值的 Get 方法** -- `Version::Get()`
- 其次是为上层调用提供了 **收集当前版本所有文件的迭代器** -- `Version::AddIterators()` 
- 为合并文件提供了 **判断键值范围与文件是否有交集** 的辅助函数 -- `Version::OverlapInLevel()`
- 最后是提供了 memtable compact 的 level -- `Version::PickLevelForMemTableOutput()`

Version 是管理某个版本的所有 sstable 的类,就其导出接口而言,无非是遍历 sstable,查找 k/v。以及为 compaction 做些事情,给定 range,检查重叠情况。

![](./assets/Version_Builder_view_11_12.jpg)

&nbsp;   

### VersionSet
管理整个 db 的当前状态,负责包括 Log, Compaction, Recover 等。

![](./assets/VersionSet_11_08.png)


&nbsp;   
<a id="dbimpl"></a>
## [DBImpl](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/DBImpl%20-%202018-11-14%20-%20rsy.md)

- 实现了所有面对用户的接口
- 负责 Recover,Log,Compaction 等

![](./assets/LevelDB-BackgroundCompaction-Processes_11_12.jpg)

================================================
FILE: architecture/DB/Recover - 2018-11-08 - rsy.md
================================================
# Recover - 2018-11-07 - rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/version_set.h`, `db/version_set.cc`, `db/db_impl.h`, `db/db_impl.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

数据库每次启动时,都会有一个 recover 的过程,简要地来说,就是利用 Manifest 信息重新构建一个最新的 version。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](./assets/version_recover_10_12.jpeg)

### 调用流程:

- `DB::Open()`:
  - `DBImpl::Recover()`:
      - `VersionSet::Recover()`:从 CURRENT 读 Manifest (VersionEdit)
      - 如果有 log 比 Manifest 文件中记录的 log 要新,那说明上次没来得及从内存 dump,于是从 log 回复数据,调用 `RecoverLogFile()`
      - `DBImpl::RecoverLogFile()`:打开指定的 log 文件,回放日志。期间可能会执行 compaction,产生新的 level-0 sstable 文件(`DBImpl::WriteLevel0Table(MemTable*, VersionEdit*, Version*)`),记录文件变动到 edit 中

> `DBImpl::RecoverLogFile()`:  
> &emsp;&emsp;&emsp;&emsp; `log_number` -> `LogFileName` -> `SequentialFile` -> `log::Reader` -> `record (Slice)` -> `WriteBatch` -> `MemTable`->   
> &emsp;&emsp;&emsp;&emsp; 对于最后一个 log,&emsp;(1) 继续用 `mem` 和 log    
> &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;(2) 不继续用,就 cmpact,`WriteLevel0Table()` 并且信息存入 VersionEdit


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [跟吉姆一起读LevelDB 4.数据库恢复(1)](https://zhuanlan.zhihu.com/p/27400189)
- [跟吉姆一起读LevelDB 5.数据库恢复(2)](https://zhuanlan.zhihu.com/p/27417009)
- [跟吉姆一起读LevelDB 6.数据库恢复(3)](https://zhuanlan.zhihu.com/p/27467584)
- [leveldb - recovery](https://dirtysalt.github.io/html/leveldb.html#org97d0701)
- [leveldb源码分析—Recover和Repair](https://www.cnblogs.com/KevinT/p/3875572.html)

================================================
FILE: architecture/DB/Slice - 2018-09-16 - rsy.md
================================================
# Slice - 2018-09-16 rsy

Slice (include/leveldb/slice.h) 

为操作数据的方便,将数据和长度包装成 Slice 使用,直接操控指针避免不必要的数据拷贝。

概括地讲:`leveldb::Slice` 是个轻量级的 `std::string`.

![](assets/Slice_09_16.png)

Specification:

1. 所有操作均有边界检查。
2. `void remove_prefix(size_t n)`:向后移动 n 个byte, size 减少 n.
3. `bool starts_with(const Slice& x)`:判断前缀是否与 x 吻合。

================================================
FILE: architecture/DB/Snapshot - 2018-11-07 - rsy.md
================================================
# Snapshot - 2018-11-07 - rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`include/leveldb/snapshot.h`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

依赖于 `SequnceNumber` 来标识时间点, leveldb 中 `Snapshot` 的实现很简单,只需要记录产生 `Snapshot` 时的 `SequnceNumber` 即可, 所有 `Snapshot` 用 double-linked list 组织,新加入的添加在列表头。

> `Snapshot` 集合在 leveldb 里面组织成为一个链表,oldest 的节点必然最小的 snapshot。对于每一个 snapshot 配备一个 sequence number, 所以很明显 oldest 的节点的 sequence number 应该是最小的。每次进行 compaction 的时候会判断当前最小的 sequence number 是多少然后**将一些不必要的节点删除**。另外在查询 key 的时候也会结合这个 snapshot sequence number **结合成为一个复合 key 进行查询**。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](./assets/Snapshot_UML_11_07.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

`SnapshotList`:维护一个双向链表

- `Delete(SnapshotIml*)`:
- `New(SequenceNumber)`:
- `empty()`:
- `newest()`:
- `oldest()`:


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

`DBImpl` 含有一个 `SnapshotList`,用来维护用户的快照。

- `DB::GetSnapshot()`:用户对当前 DB 生成快照
- `DB::ReleaseSnapshot(result)`:当用户弃置快照时调用,**DB 对快照资源不管理**


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

用户生成快照时,调用 `DB::GetSnapshot()`,即提供一个目前最新的 sequence number,添加到尾巴。`prev_` 是最新的,`next_` 是 sequence number 增大的方向。

&nbsp;   

另外,注意到 `SnapshotList` 没有禁用 copy ctor,也没写 dtor。   
在 `DB::GetSnapshot()` 上的注释中表明:  
> The caller must call `DB::ReleaseSnapshot(result)` when the snapshot is no longer needed.

所以其实也不用担心,因为资源由用户生成(提供 sequence number),也由用户释放。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [跟吉姆一起读LevelDB 10.Delete与Snapshot](https://zhuanlan.zhihu.com/p/27519715)
- [leveldb源码分析之快照SnapShots](http://luodw.cc/2015/10/31/leveldb-15/)

================================================
FILE: architecture/DB/Status-2018-10-02-dz.md
================================================
# Status-18-10-02 杜致

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)

&nbsp;

&nbsp;  <a id="module_info"></a>

## 模块信息

include/leveldb/status.h

include/util/status.cc

&nbsp;

<a id="module_in_brief"></a>

## 模块概要

Status类封装对db操作返回的结果,除success,都可以包含错误码和两条错误信息。

&nbsp;

<a id="module_function"></a>

## 模块功能

创建表示状态的对象,支持copy特定Status对象,支持输出错误码和错误信息。



保有一个const char数组state_来存储错误信息。定义enum类型code,0表示success,1-5为错误码。

state\_[0..3]存储消息的长度,state\_[4]存储 code,state\_[5..]存储消息。

在不同线程调用其const method时无需外同步。

&nbsp; &nbsp;

<a id="interface_specification"></a>

## 接口说明
![](assets/Status_10_02.png)

Status() 创建一个success状态,state\_初始化为null。\~Status() 析构函数回收state_。

提供一串static函数创建code不同的error状态,可以传入一条或两条错误信息(slice)。

提供一串用于判断Status对象code的值的函数。

可以通过重载的构造函数或操作符'='来copy Status对象的内容。

string ToString() const 函数,在状态为success时输出”OK“,否则输出”error code: "+"msg: "+"msg2"。

&nbsp;

<a id="dependency_specification"></a>

## 相关依赖说明

需要定义 "leveldb/export.h" "leveldb/slice.h"

操作如Open, Delete, Put, Get, CreateDir, BuildTable, DestroyDB...都要返回状态。

&nbsp;

<a id="inner_detail"></a>

## 内部实现细节

存储错误信息的state_数组代替std::string节省空间。

在ToString() 中检查code,若是未知的值,使用snprintf输出错误消息,snprintf比sprintf安全,防止缓冲区溢出。

错误码:

```
enum Code {
    kOk = 0,
    kNotFound = 1,
    kCorruption = 2,
    kNotSupported = 3,
    kInvalidArgument = 4,
    kIOError = 5
  };
```

&nbsp;

<a id="inner_detail"></a>

## 参考资料

[LevelDB : Status](https://blog.csdn.net/huntinux/article/details/51725569)

[LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)


================================================
FILE: architecture/DB/TableCache - 2018-09-30 - rsy.md
================================================
# TableCache - 2018-09-30 rsy


- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

TableCache (`db/table_cache.cc`, `db/table_cache.h`)   


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

`TableCache` 缓存 `Table` 对象,每个DB一个。

`TableCache` 的 KV 格式:

- 以 `file_number` 作 key
- 以 `TableAndFile` 对应的预加载索引作为 value。

&nbsp;   
<a id="module_function"></a> 
## 模块功能

![](assets/TableCache_UML_09_26.png)

table cache 缓存的是 sstable 的索引数据

来看 `TableCache`,先简单梳理下到 `TableCache` 的查找流程,用户提交key查询,交由 `Status DBImpl::Get()` ,获取两种 `MemTable` 和当前 `Version`,依次查询 `memtable`、`immutable memtable` ,未找到则在当前 `Version`上 `Status Version::Get()`,依次从最低level到最高level查询直至查到,在每层确定可能包含该key的 SSTable 文件后,就会在所属 `VersionSet` 的 `table_cache` 中继续查询,即调用 `Status TableCache::Get()`

另外,`TableCache` entry 的插入在 Compaction 时也有体现,每当通过 Compatction 生成新的 SSTable 文件,也会以验证正常可用的方式更新该 SSTable 的索引信息到 `TableCache`。


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- Evict(文件号):清除指定文件所有的 cache 和 entry
- Get():这是一个查找函数,如果在指定文件中 seek 到 internal key "k" 找到一个 entry,就调用 `(*handle_result)(arg, found_key, found_value)`
- NewIterator():为指定的 file 返回一个 `iterator`,


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

被 `class DBImpl` private含有




&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`TableCache::Get()`:   
首先根据 `file_number` 找到 Table 的 cache 对象(获取该 key 所属 SSTable 的句柄后,调用 `Status Table::InternalGet()` 查找真正包含该 key 的数据块),如果找到了就调用 `Table::InternalGet()`,对查找结果的处理在调用者传入的 saver 回调函数中。Cache 在 Lookup 找到 cache 对象后,如果不再使用需要调用 `Release()` 减引用计数。
>**疑惑**:为什么最后要进行 `Release()`,可能和 TwoLevelCache 的读取和更新有关,回头补上。。。


&nbsp;   
`TableCache::NewIterator()`:   
调用 `FindTable()`,返回 `Handle` 对象,取出 `Table` 对象,并创建一个 `Iterator`,并注册一个回调的 cleanup 函数。(**实际上经常用于添加 sstable 到 TableCache**)


&nbsp;    
`TableCache::Evict()`:   
根据 `file_number` 清除 cache 对象。


&nbsp;   
`TableCache::FindTable()`:   
先根据 `file_number` 在 cache 中查

- 如果有,返回ok
- 否则(即未命中)创建 `TableAndFile`,即 `RandomAccessFile` 和 `Table`(`Table` 文件格式为 <db_name><file_number%6>),然后调用 `Table::Open()`(并将.sst文件映射到table,Table类用于解析.sst文件) 并加入缓存。



&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb源码分析之Table_cache](http://luodw.cc/2015/10/25/leveldb-13/)
- [levelDB源码分析-TableCache](https://blog.csdn.net/tankles/article/details/7663929)
- [LevelDB源码分析之十五:table cache](https://blog.csdn.net/caoshangpa/article/details/79062784)
- [SSTable之两级Cache-leveldb源码剖析(8)](http://www.pandademo.com/2016/04/two-stage-cache-of-sstable-leveldb-source-dissect-8/)
- [Leveldb源码分析--11](https://blog.csdn.net/sparkliang/article/details/8740712)

================================================
FILE: architecture/DB/ValueType & SequenceNumber 2018-11-07 - rsy.md
================================================
# ValueType & SequenceNumber - 2018-11-07 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/dbformat.h`

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

- `ValueType`:顺序写(sequential writing)的策略使得数据不可被更改,于是标记 “delete” 操作。占 **8** bits。在顶层调用 `Put()` 或 `Delete()` 后转发给 [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md),在每一个 K-V `record` 中存储。
- `SequenceNumber`:标识每一次更新的版本。占 **56** bits。在 [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md) 中记录(为一批 `record`)。

> 我的理解是 SequenceNumber 是可以重复的,因为 [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md) 中是批量写入,所以每一批的 SequenceNumber 都是一样的。
> 
> **上面这一句是错的!!!**读了 `DBImpl::Write()`,虽然 sequence number 只在 WriteBatch 的第一个存储,但是写完后 `last_sequence += WriteBatchInternal::Count()`,表明这个 WriteBatch 中的 record 按顺序增加 sequence number

[WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md) 在 `InsertInto()` 中写入 memtable,`InsertInto()` 先拿出来 sequence number,然后中调用 `Iterate()` 不断迭代地将 batch 中的数据写入(每次调用 `Memtable::Add()`,**sequence 自增**)。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)

================================================
FILE: architecture/DB/Version & VersionSet - 2018-11-12 - rsy.md
================================================
# Version & VersionSet - 2018-11-12 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/version_set.h`, `db/version_set.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

### 版本控制

保留旧的数据,是为了支持旧版本的查询操作(compact 时发生读操作;不会发生旧版本的写,因为 sequential-writing),于是自然地引入了版本控制的概念。

![](./assets/Version_UML_11_12.png)

![](./assets/Compaction_UML_11_12.png)

![](./assets/VersionSet_UML_11_12.png)

&nbsp;   
<a id="module_function"></a>
## 模块功能

leveldb 对单版本的 sstable 文件管理,主要集中在 Version 类中。**Version 不会修改其管理的 sstable 文件,只有读取操作**。

将每次 compact 后的最新数据状态定义为 Version,也就是当前 db 元信息以及每个 level 上具有最新数据状态的 sstable 集合。 compact 会在某个 level 上新加入或者删除一些 sstable,但可能这个时候,那些要删除的 sstable 正在被读,为了处理这样的读写竞争情况,基于 sstable 文件一旦生成就不会改动的特点,每个 Version 加入引用计数,读以及解除读操作会将引用计数相应加减一。 这样, db 中可能有多个 Version 同时存在(提供服务) ,它们通过链表链接起来。当 Version 的引用计数为 0 并且不是当前最新的 Version 时,它会从链表中移除, 对应的, 该 Version 内的 sstable 就可以删除了(这些废弃的 sstable 会在下一次 compact 完成时被清理掉)。

![](./assets/Version_VersionEdit_11_08.png)

### Version 版本类

这个类主要功能

- 首先是提供了在**当前版本搜索键值的 Get 方法** -- `Version::Get()`
- 其次是为上层调用提供了 **收集当前版本所有文件的迭代器** -- `Version::AddIterators()` 
- 为合并文件提供了 **判断键值范围与文件是否有交集** 的辅助函数 -- `Version::OverlapInLevel()`
- 最后是提供了 memtable compact 的 level -- `Version::PickLevelForMemTableOutput()`

Version 是管理某个版本的所有 sstable 的类,就其导出接口而言,无非是遍历 sstable,查找 k/v。以及为 compaction 做些事情,给定 range,检查重叠情况。

![](./assets/Version_Builder_view_11_12.jpg)

&nbsp;   

### VersionSet

管理整个 db 的当前状态,负责包括 Log, Compaction, Recover 等。

> **注意,下面这个图有误,dummy 之后的 Version 是向后添加,current 总是指向 最后一个 Version**    
> 当然你也可以说 prev 指针向右。。。

![](./assets/VersionSet_11_08.png)

![](./assets/LevelDB-BackgroundCompaction-Processes_11_12.jpg)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `Version::Get()`:查找 key,存到 value
- `Version::AddIterators()`:为该 Version 中的所有 sstable 都创建一个 Two Level Iterator,以遍历 sstable 的内容
- `Version::GetOverlappingInputs()`:找出指定 level 上与指定区间[`begin`, `end`] overlap 的所有 file
- `Version::OverlapInLevel()`:检查指定 level 中的某些文件是否和 [`smallest_user_key`, `largest_user_key`] 有 overlap
- `Version::PickLevelForMemTableOutput()`:返回我们应该在哪个 level 上放置新的 memtable compaction,这个 compaction 覆盖了范围 [`smallest_user_key`, `largest_user_key`](也就是说 **Memtable Compaction 不一定放在 level-0**)

> Memtable Compaction 不一定放在 level-0,如果 mem 和 level-0、level-1 都没有交集,和 level-2 有交集,就放在 level-1。   
> 也就是说:**如果靠上的 level 和 mem 都没有交集,那么就尽量把 mem 向下 dump**。估计是为了减小 level-0 做 compaction 的压力。

<a></a>

- [`class VersionSet::Builder`](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Manifest%20%26%20VersionEdit%20-%202018-11-07%20-%20rsy.md):对 Version 应用 VersionEdit 增量
- `VersionSet::Finalize(Version*)`:计算下一次 compaction 最合适的 level
- **`VersionSet::LogAndApply()`**:**应用 VersionEdit 增量,刷新、写入 Manifest 文件,每次 compaction 时调用**
- `VersionSet::CompactRange()`:返回一个 Compact*,用于 manual compact
- `VersionSet::PickCompaction()`:返回一个 Compact*,用于自动触发的 compaction;`CompactRange()` 是手动触发
- `VersionSet::Recover()`:从 CURRENT 读 Manifest (VersionEdit)
- `VersionSet::MakeInputIterator(Compaction*)`:针对 compaction,返回用于遍历多个 sstable 文件的 iterator


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

### `Version::Get()` 调用层次
`DB::Get()` 即 `DBImpl::Get()`,即用户发起查询:

- 先在 mem 找
- 否则在 imm 找
- 否则在 current 找(调用 `TableCache::Get()`),并有可能触发 compaction

### `Version::AddIterators()` 调用层次
`DB::NewIterator()` 即 `DBImpl::NewIterator()` -> `DBImpl::NewInternalIterator()`(加入 mem, imm, version.current 的 iter) -> `Version::AddIterators()`(这里加入 version.current 的 iter)  

### `Version::PickLevelForMemTableOutput()` 调用层次
`DBImpl::CompactMemTable()` / `DBImpl::RecoverLogFile()` -> `DBImpl::WriteLevel0Table` -> `Version::PickLevelForMemTableOutput()`

### `VersionSet::CompactRange()` 调用层次
`DBImpl::MaybeScheduleCompaction()` (schedule 后台线程) -> `DBImpl::BackgroundCompaction()` (**手动** compaction) -> `VersionSet::CompactRange()`

### `VersionSet::PickCompaction()` 调用层次
`DBImpl::MaybeScheduleCompaction()` (schedule 后台线程) -> `DBImpl::BackgroundCompaction()` (**自动触发** compaction) -> `VersionSet::PickCompaction()`

### `VersionSet::Recover()` 调用层次
`DBImpl::Open()` -> `DBImpl::Recover()` -> `VersionSet::Recover()`

### `VersionSet::MakeInputIterator(Compaction*)` 调用层次

`DBImpl::BackgroundCompaction()` -> `DBImpl::DoCompactionWork(CompactionState*)` -> `VersionSet::MakeInputIterator(Compaction*)`


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

### `Version::Get()`

- 对于 level-0,有可能 overlap,所以把符合范围(`smallest.user_key` ~ `largest.user_key`)的 file 都收集起来,并且按照 sequence number 排序
- 对于其他 level,直接二分查找

### `Version::AddIterators()`

- 对于 level-0,把所有 sstable 的 iter 都加入(因为有可能overlap)
- 对于其他 level,生成连续的 iter(并且是 lazy open)

然后调用 `TableCache::Get()` 查找

### `Version::PickLevelForMemTableOutput()`
- 如果和 level-0 有交集,就选 level-0
- 否则就尽量向下 dump,直到下一层与 memtable 有交集
- 中间还有个 判断两层之后的玩意,估计是为了减小后面的 overlap,降低 compaction 压力

> 我才知道原来 Memtable Compaction 不一定 dump 到 level-0

### `VersionSet::Finalize(Version*)`
- 对于0层文件,该层的分数为 文件总数 / 4
- 对于非0层文件,该层的分数为 文件数据总量 / 数据总量上限

将得分最高的层数记录,若该得分超过1,则为下一次进行合并的层数。

### `VersionSet::PickCompaction()`
分为 size compaction 和 seek compaction,不过更倾向于 size compaction(原因我猜可能是有了 BloomFilter)

### `VersionSet::Recover()`
参考 [Recover](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Recover%20-%202018-11-08%20-%20rsy.md#%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B)

### `VersionSet::MakeInputIterator(Compaction*)`

- 对于 level-0 的 sstable 文件,每一个文件作为一个 iterator 存在
- 对于 level>0 的 sstable 文件,使用 `NewTwoLevelIterator()` 包装 `Version::LevelFileNumIterator()` 来当做这一层的 iterator


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [LevelDB源码解析6. 版本概念](https://zhuanlan.zhihu.com/p/34674504)
- [LevelDB源码解析10.创建VersionSet](https://zhuanlan.zhihu.com/p/35275467)
- [LevelDB源码解析14. Version的数据结构](https://zhuanlan.zhihu.com/p/44584617)
- [LevelDB源码解析15. Version的数据结构2](https://zhuanlan.zhihu.com/p/44639316)
- [leveldb源码剖析---版本管理](https://blog.csdn.net/Swartz2015/article/details/68062639)
- [庖丁解LevelDB之版本控制](http://catkang.github.io/2017/02/03/leveldb-version.html)
- [leveldb源码分析之version、version_edit和version_set](http://luodw.cc/2015/10/31/leveldb-16/)
- [版本控制VersionEdit-leveldb源码剖析(13)](http://www.pandademo.com/2016/05/version-control-versionedit-leveldb-source-dissect-13/)
- [浅析 Bigtable 和 LevelDB 的实现](https://draveness.me/bigtable-leveldb)

================================================
FILE: architecture/DB/WriteBatch - 2018-10-01 - rsy.md
================================================
# WriteBatch - 2018-10-01 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`include/leveldb/write_batch.h`, `db/write_batch_internal.h`, `db/write_batch.cc`

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

leveldb 内部的一个批量写的结构,在 leveldb 为了提高插入和删除的效率,在其插入过程中都采用了批量集合相邻的多个具有相同同步设置的写请求以批量的方式进行写入。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/WriteBatch_UML_10_01.png)

![](assets/WriteBatch_rep_content_10_01.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `Put()`:存储 put 操作
- `Delete()`:存储 delete 操作
- `Iterate()`:遍历内容 经handler接口 存到 memtable 中

`class WriteBatchInternal`:工具类

- `WriteBatchInternal::Count()`:返回个数
- `WriteBatchInternal::SetCount()`
- `WriteBatchInternal::Sequence()`:返回 sequence number
- `WriteBatchInternal::SetSequence()`
- `WriteBatchInternal::Contents()`:返回内容
- `WriteBatchInternal::SetContents()`
- `WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src)`:拼接两个 WriteBatch
- `WriteBatchInternal::InsertInto(const WriteBatch* b, MemTable* memtable)`:将 WriteBatch 写入 memtable,转发给了 `WriteBatch::Iterate()`


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

上层方法调用:当插入数据时,`DBImpl::Put()` 和 `DBImpl::Delete()` 调用 `WriteBatch::Put()` 和 `WriteBatch::Delete()` 方法将记录添加进 `WriteBatch::rep_`,然后 `DBImpl::Write(WriteBatch*)` 调用 `WriteBatchInternal::InsertInto()` 方法。

数据库流程:先是调用 `DB::Put()` 和 `DB::Delete()` 方法,这两个方法再调用 `Write()` 将数据写到 `memtable`。`Write()` 最终是先将 `WriteBatch::rep_`中的记录先添加进 log 文件,最后调用 `WriteBatchInternal::InsertInto()` 方法,将记录添加进 memtable。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`class WriteBatchInternal` 封装了辅助操作 `WriteBatch` 的非公开接口。

`Status WriteBatchInternal::InsertInto(const WriteBatch* b, MemTable* memtable)`:  
&emsp;  将 `WriteBatch` 内容全部插入到 `MemTable` 里面去,调用 `WriteBatch::Iterate()`

&nbsp;   

**`WriteBatch::Iterate()`**:从 writebatch 写入 memtable

- 先拿出来 sequence number
- 然后将 k-v 不断迭代地插入 memtable,调用 `Memtable::Add()`
  - `mem_->Add(sequence_, kTypeValue, key, value); ` ,sequence 自增
  - `mem_->Add(sequence_, kTypeDeletion, key, Slice());`,sequence 自增


&nbsp;   

存储格式:

    // WriteBatch::rep_ :=
    //      sequence: fixed64
    //      count: fixed32
    //      data: record[count]
    // record :=
    //      kTypeValue varstring varstring 
    //      kTypeDeletion varstring
    // varstring :=
    //      len: varint32
    //      data: uint8[len]

&nbsp;   

`size_t WriteBatch::ApproximateSize();` 的实现是假的,而且也并没有被使用。

&nbsp;   

`Status WriteBatch::Iterate(Handler* handler) const;`:遍历内容存放到 handler 对象里面。

&nbsp;   

>**(转)**为什么 leveldb 采用了这样的其中一个线程去批量操作而其他线程进行等待的方式呢?我们知道 leveldb 在实际插入过程中会有一系列的判断,和写日志到磁盘的操作。而首先这些判断都是在保持锁的情形下进行的,这里其实也就注定了只能是串行的;其次对于写磁盘,将多次写合并为一次写入会显著的提高效率,相当于 N * 磁盘寻址时间 + 写入时间总和变为了 一次磁盘寻址时间 + 写入时间总和,即节省了(N - 1)次磁盘寻址时间。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [leveldb源码分析--WriteBatch](https://www.cnblogs.com/KevinT/p/3813635.html)
- [leveldb源码分析之WriteBatch](http://luodw.cc/2015/10/30/leveldb-14/)
- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/rwopt.html#batch)
- [leveldb](https://dirtysalt.github.io/html/leveldb.html#orgf584809)

================================================
FILE: architecture/DB/dbformat_key-2018-10-01-ss.md
================================================
# Module - 2018-10-01 苏胜

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息
ParsedInternalKey(db/dbformat.h,db/dbformat.cc)

InternalKey(db/dbformat.h,db/dbformat.cc)
 
LookupKey(db/dbformat.h,db/dbformat.cc)

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要
1.ParsedInternalKey是db内部操作的key。
 
2.InternalKeydb内部,包装易用的结构,包含userkey与SequnceNumber/ValueType。

3.LookupKey是db内部在为查找memtable/sstable方便,包装使用的key结构,保存有userkey与SequnceNumber/ValueType dump 在内存的数据。
   
<a id="module_function"></a>
## 模块功能

![](assets/ParsedInternalKey_10_01.png)

ParsedInternalKey(struct ParsedInternalKey)就是对 InternalKey 分拆后的结果,先来看看 ParsedInternalKey 的成员,

这是一个 struct{

Slice user_key; 

SequenceNumber sequence; 

ValueType type;
}
 
![](assets/InternalKey_10_01.png)

class InternalKey 是一个只存储了一个 string,它使用一个 DecodeFrom() 函数将 Slice 类型的 InternalKey 解码出 string 类型的 InternalKey。  

void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } 
 
也就是说 InternalKey 是由 User_key.data + SequenceNumber + ValueType组合而成的。

InternalKey 的格式为: | User_key.data (string)| sequence number (7 B) | value type (1 B) | 

![](assets/LookupKey_10_01.png)

Memtable 的查询接口传入的是 LookupKey(class LookupKey),它也是由 User Key 和 Sequence Number 组合而成的,

从其构造函数:LookupKey(const Slice& user_key, SequenceNumber s)中分析出 LookupKey 的格式为: 

| Size (Varint32)| User key (string) | sequence number (7 bytes) | value type (1 byte) | 

两点: 1.这里的 Size 是 user key 长度+8,相当于 InteralKey 的长度;value type 是 kValueTypeForSeek,它等于 kTypeValue。

2.由于 LookupKey 的 size 是变长存储的,因此它使用 kstart_记录了 user key string的起始地址,否 则将不能正确的获取 size 和 user key; 

&nbsp;   
<a id="interface_specification"></a>
## 接口说明
1.ParsedInternalKey

DebugString()用于处理ParsedInternalKey成字符串

2.InternalKey

DecodeFrom() 函数将 Slice 类型的 InternalKey 解码出 string 类型的 InternalKey.

Clear()函数清除InternalKey包含的数据

SetFrom()函数创建新的InternalKey结构

3.LookupKey

Slice memtable_key() const { return Slice(start_, end_ - start_); }  
返回一个适合MemTable查找的key。

Slice internal_key() const { return Slice(kstart_, end_ - kstart_); }      
返回一个internal key(适用于传递给内部迭代器)

Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } 返回user key。
其中 start_是 LookupKey 字符串的开始,end_是结束,kstart_是 start_+ sizeof(varint32),也就是 user key 字符串的起始地址。


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明
需要Slice类型的定义和用法
Slice (include/leveldb/slice.h)

&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

LookupKey: 

start:  userkey_len  (varint32)   

kstart:  userkey_data (userkey_len)

end:  SequnceNumber/ValueType (uint64)

对 memtable 进行 lookup 时使用 [start,end], 对 sstable lookup 时使用[kstart, end]。 


&nbsp;   
<a id="reference"></a>
## 参考资料
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析 - 百度文库 100多页..................](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)


================================================
FILE: architecture/SSTable/Block - 2018-10-02 - rsy.md
================================================
# Block - 2018-10-02 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`table/block.h`, `table/block.cc`, `table/block_builder.h`, `table/block_builder.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

`sstable` 中的数据以 `Block` 单位存储,有利于 IO 和解析的粒度。    
提供遍历迭代器(遍历k-v)。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/Block_UML_10_02.png)

`Block` 内容:

![](assets/Block_structure_10_02.png)

保存了**数据**,**总共大小**,**每一个压缩块的偏移** 和 **总共有多少压缩块**。

- 总共有多少压缩块:读最后一个32位int

每一个 `Record` 内容格式:

![](assets/Block_data_format_10_02.png)   
每一个 record 群中,都对 `key` 的前缀进行了压缩,所以有共享部分和非共享部分。

![](assets/data_block_record_11_12.png)

迭代器 `Block::Iter`:

![](assets/Block_iter_UML_10_02.png)

`BlockBuilder`:

![](assets/Block_Builder_UML_10_02.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

`Block` 的创建:   

`BlockBuilder` 调用 `Add()` 添加 k-v,然后调用 `Finish()` 生成 `BlockContents`。


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

通过 `BlockHandle` 可定位到该 `Block` 在 sstable 中的 offset 及 size,从而读取出具体的 `Block`(`ReadBlock()`),并解析。   
上层对 `Block` 进行 `key` 的查找和遍历,封装成 `Block::Iter` 处理。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

**每一组 record 的个数是由 `options_->block_restart_interval` 决定的。**


&nbsp;   
**`Block::Iter`** :   
应该是为了复用,把所有解析操作都包装在 `Iter::ParseNextKey()` 里面,其他函数基本只是确定 offset,然后调用 `Iter::ParseNextKey()`。    
整个迭代器的遍历操作就是维护大块中小块的位置。   

- `Iter::ParseNextKey()`:向下 decode 出一个 record,并把 `key` 和 `value` 都解析完成。(**注:`key` 的共享长度在每个 record 集合内是非递增的(除了第一个共享长度为 0),因为数据存储保证 "字符串是按字典序递增的",见 `BlockBuilder::Add()`,所以每次只要把 `key` 的长度缩小成当前的共享长度,然后加上非共享部分**)
- `Iter::Seek()`:二分查找
  - 索引是每一个 `restart`
  - 找到第一个 `key >= target`


&nbsp;    
**`BlockBuilder`**:

- `BlockBuilder::Finish()`:将 `restrat_nums` 和 `restart_size` 添加到 `buffer` 末尾,返回 `BlockContents` 即 `Block` 中的 `data`。
- `BlockBuilder::Add()`:(要求添加的 `key` 是递增的,顺序是上层保证的,由 memtable-dump 或 compact-merge 产生)维护成员 `last_key_`,每次取出 `shared`(如果达到上限就是 0,新开一个 `restart`),然后
> // Add "<shared><non_shared><value_size>" to buffer_   
> // Add string delta to buffer_ followed by value   
> // Update state   维护 last_key_ 和 conuter_


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [leveldb](https://dirtysalt.github.io/html/leveldb.html#orgcf948fc)
- [LevelDB源码分析之十二:block](https://blog.csdn.net/caoshangpa/article/details/78977743)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb源码分析--SSTable之block](https://www.cnblogs.com/KevinT/p/3816794.html)
- [LevelDB源码解析17.SST里的Block](https://zhuanlan.zhihu.com/p/45125278)
- [LevelDB源码解析18.Block的Iterator](https://zhuanlan.zhihu.com/p/45217164)
- [LevelDB源码解析20. BlockBuilder](https://zhuanlan.zhihu.com/p/45523142)
- [LevelDB源码解析21. 读取Block](https://zhuanlan.zhihu.com/p/45524806)

================================================
FILE: architecture/SSTable/BlockHandle&Footer - 2018-10-03 - rsy.md
================================================
# BlockHandle & Footer - 2018-10-03 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`table/format.h`, `table/format.cc`

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/BlockContent_UML_10_03.png)   
![](assets/BlockHandle_UML_10_03.png)   
![](assets/Footer_UML_10_03.png)   

- `BlockContent` 保存了来自 `BlockBuilder::Finish()` 返回的 block 数据
- `Block` 的元信息(位于 sstable 的 offset/size) 封装成 `BlockHandle`
- `Footer` 用来存储 `meta index block` 与 `index block` 在 sstable 中的索引信息,另外尾部还会存储一个 `magic word`,内容为:http://code.google.com/p/leveldb/ 字符串sha1哈希的前8个字节


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

`ReadBlock()`:从随机文件里面读取 `Block` 出来,对于这个 `Block` 的位置的话由 `handle` 提供。注意每个 `Block` 后面还有 `crc` 和 `type`,然后将读取的 `Block` 二进制构造 `Block` 对象返回。


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

在 `Table` 中调用 `ReadBlock()` 读取。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`SSTable` 文件格式:   
![](assets/SSTable_structure_10_03.png)

- `metaindex_handle_`:数据区(data block)索引
- `index_handle_`:meta block 的索引。


&nbsp;   
**`Footer`**:   
![](assets/Footer_structure_10_03.png)   
`Footer` 固定 48B,分别存储 `metaindex_handle` 和 `index_handle` 的当前编码 offser 和 size,相当于 `metaindex` 和 `index` 的索引部分,最后加入8字节的小端编码的 `magic-number`。

- 落盘时会调用 `Footer::EncodeTo()`
- 读取时调用 `Footer::DecodeFrom()`


&nbsp;   
**`ReadBlock()`**:   

参考 [LevelDB源码解析21. 读取Block](https://zhuanlan.zhihu.com/p/45524806)


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb - BlockHandle](https://dirtysalt.github.io/html/leveldb.html#org70e7f16)
- [leveldb - Footer](https://dirtysalt.github.io/html/leveldb.html#org681caf8)
- [leveldb - ReadBlock](https://dirtysalt.github.io/html/leveldb.html#orgf546f00)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码解析21. 读取Block](https://zhuanlan.zhihu.com/p/45524806)
- [leveldb-handlebook footer](https://leveldb-handbook.readthedocs.io/zh/latest/sstable.html#footer)
- [SSTable之文件格式及生成-leveldb源码剖析(7)](http://www.pandademo.com/2016/04/sstable-file-format-and-build-leveldb-source-dissect-7/)
- [LevelDB源码解析16.SST文件格式](https://zhuanlan.zhihu.com/p/44954881)

================================================
FILE: architecture/SSTable/Compaction - 2018-10-05 - rsy.md
================================================
# Compaction - 2018-10-05 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`db/version_set.h`, `db/version_set.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/Compaction_UML_10_5.png)

- LevelDB 存储分为两部分,一部分在内存,另一部分在磁盘上。内存中方便快速查找, 查找失败后,去磁盘上查找。一段时间后或内存达到一定大小,会将内存中的 compact 成 .sst 文件存在磁盘
- compaction 是执行 LSM-tree 中 merge 的过程
- 删除操作在 memtable 只是打上删除标记,**真正的删除** 在 compaction 中做
- **minor compaction** 用于内存到外存的迁移过程。level-0 是将 memTable 整个 dump 出来的结果,因此可能之间有相交,这个操作叫 minor compaction
- **major compaction** 用于 level 之间的迁移。major compaction 是将 层i 合到 层i+1 中去,合并是将 层i 中的一块合到整个 层i+1 中去, 这个过程类似归并排序,将参与的几个块全部取出来排序,再重新组合成新的 层i+1,同时 会将重复的 key 弃掉(弃掉是指如果 key 已经出现在更低的层,则高级的层不需要记录这个 key)
  - 对 level > 0 的 sstables,选择其中一个 sstable 与 下一层 sstables 做合并
  - 对 level-0 的 sstables,在选择一个 sstable 后,还需要找出所有与这个 sstable 有 key 范围重叠的 sstables,最后统统与 level-1 的 sstables 做合并


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](./assets/LevelDB-BackgroundCompaction-Processes_11_12.jpg)

当 level-L 的总文件大小查过限制时,我们就在后台执行 compaction 操作。 Compaction 操作从 level-L 中选择一个文件 f,以及选择中所有和 f 有重叠的文件。如果某个 level-(L+1) 的文件 ff 只是和 f 部分重合,compaction 依然选择 ff 的完整内容作为输入,在 compaction 后 f 和 ff 都会被丢弃。并且确保 level-(L+1) 与 level-(L+2) 

### Minor Compation

![](assets/Minor_Compaction_10_05.png) 

### Major Compaction

![](assets/Major_Compaction_10_05.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `Compaction::IsTrivialMove()`:是否可以 “简单地向下层移动文件,而不用 compaction”
- `Compaction::AddInputDeletions()`:通知这次操作做了文件的删除,将这些可以删除的文件填写到 `VersionEdit` 里面
- `Compaction::IsBaseLevelForKey()`:判断某个 key 在高层是否还存在
- `Compaction::ShouldStopBefore()`:如果某个 key 和下层重合过多就停止

<a></a>

- `DBImpl::CompactMemTable()`:将 memtable dump 成 level-0
- `DBImpl::BackgroundCompaction()`:后台做 major compaction


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

会触发 compact 的情况:

- `DB::Open()`
- `DBImpl::Get()`:Get 操作时,如果有超过一个 sstable 文件进行了 IO,会检查做 IO 的最后一个文件是否达到了 compact 的条件(allowed_seeks 用光,参见 Version),达到条件,则主动触发 compact
- `DBImpl::Write()`:每次写操作时检查,如果发现 memtable 已经写满并且没有 immutable memtable,会将 memtable 置为 immutable memtable,生成新的 memtable,同时触发 compact


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

- `DBImpl::CompactMemTable()`:把 imm 写入 level-0 并删除
- `DBImpl::BackgroundCompaction()`:
  - 如果 imm 非空,就只写 level-0
  - 否则就是 trivial 或者 调用 `DoCompactionWork()`
- `DBImpl::DoCompactionWork()`: (转)
  - 如果和下层重合过多就停止
  - 迭代每一个需要合并的文件,删除键值相同,较早时间那个键值对
  - 删除键类型为 `kTypeDelete` 类型的键值(如果是 `kTypeDelete`,调用 `IsBaseLevelForKey()` 检查是否之前有这个 key,如果没有就可以 drop,否则现在还是记录着 `kTypeDelete`,直到这两个 compact)
  - 除此之外的 k-v 写入新生成的文件
  - 判断新生成的文件大小是否大于一定值,如果是,则将这个文件内容刷新到磁盘
  - 等所有键值都写到文件,文件都刷新到磁盘后,调用函数 `DBImpl::InstallCompactionResults()` 版本更新,因为文件个数、编号产生了变动,所以要新生成一个版本
>`DBImpl::DoCompactionWork()` 中间那个 for 循环有毒,为啥要把 next 写在最后,而不是写在 for 语句里面。

- `VersionSet::PickCompaction()`:
  - 先看是否 data 超过
  - 否则看是否 seek 超过
  - 如果是 level-0,找到所有的交集


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb-handlebook compaction](https://leveldb-handbook.readthedocs.io/zh/latest/compaction.html)
- [leveldb Compaction](https://dirtysalt.github.io/html/leveldb.html#org970cd3c)
- [存储引擎技术架构与内幕 (leveldb-1) #58](https://github.com/abbshr/abbshr.github.io/issues/58)
- [SSTable之Compaction上篇-leveldb源码剖析(9)](http://www.pandademo.com/2016/04/compaction-of-sstable-leveldb-part-1-source-dissect-9/)
- [SSTable之Compaction下篇-leveldb源码剖析(10)](http://www.pandademo.com/2016/04/compaction-of-sstable-leveldb-part-2-source-dissect-10/)
- [leveldb源码剖析----compaction](https://blog.csdn.net/Swartz2015/article/details/67633724)
- [leveldb源码分析补充之Compaction](http://luodw.cc/2015/11/04/leveldb-20/)
- [leveldb源码分析--SSTable之Compaction](https://www.cnblogs.com/KevinT/p/3819134.html)

================================================
FILE: architecture/SSTable/FilterBlock - 2018-10-03 - rsy.md
================================================
# FilterBlock - 2018-10-03 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`table/filter_block.h`, `table/filter_block.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

**`filter block` 就是 `meta block`,一般只有一个。**   
`filter block` 存储的是 `data block` 数据的一些过滤信息,用于加快查询的速度。   
一个 sstable 只有一个 `filter block`,其内存储了所有 `block` 的 filter 数据。

>索引和BloomFilter等元数据可随文件一起创建和销毁,即直接存在文件里,不用加载时动态计算,不用维护更新

把要输出的结果按照想要的格式整理好,在内存中放置好。之后由 `log_writer` 写入文件。

&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/FilterBlockBuilder_UML_10_03.png)
![](assets/FilterBlockReader_UML_10_03.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

**`FilterBlockBuilder`**:

- `StartBlock()`:不懂这个什么意思,如果 `block_size` 给的过大,那么会一直向 `filter_offsets_` 中添加同样的 offset,即不创建新的 `filter block`
- `AddKey()`:加入key
- `Finish()`:把数据拼起来返回

**`FilterBlockReader`**:

- `KeyMayMatch()`:同 `filter`,若返回 false,则一定不存在


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

把要输出的结果按照想要的格式整理好,在内存中放置好。之后由 `log_writer` 写入文件。

`FilterReader::KeyMayMatch()` 在 `Table::InternalGet()` 中调用,直接跳过没有 key match 的 block(因为 filter 返回 false 表示一定不存在)。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

**图上的 `filter_policy` 其实是 `filter_block`。**   
![](assets/Filter_Block_structure_10_03.png)

先看 `FilterBlockBuilder::Finish()`,再看 `FilterBlockBuilder::GenerateFilter()` 就懂了。   
`FilterBlockBuilder::GenerateFilter()` 就是把当前的所有 key 变成一个新的 `filter block` 然后 append 到 `result_`,然后维护一下 offset,并清楚这些 key。

&nbsp;   
**`FilterReader`**:   

- `KeyMayMatch()`:根据 `block_offset_` 去 `offset_array` 找这个 `filter block` 在数据中的偏移量,然后调用 `filter`。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [LevelDB源码解析19. FilterBlockBuilder](https://zhuanlan.zhihu.com/p/45340857)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb - FilterBlockBuilder](https://dirtysalt.github.io/html/leveldb.html#orgcda0a99)
- [leveldb - FilterBlockReader](https://dirtysalt.github.io/html/leveldb.html#orgcae4101)
- [leveldb-handlebook filterblock](https://leveldb-handbook.readthedocs.io/zh/latest/sstable.html#filter-block)

================================================
FILE: architecture/SSTable/FilterPolicy - 2018-10-03 - rsy.md
================================================
# FilterPolicy - 2018-10-03 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`include/leveldb/filter_policy.h`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

**上层用于查找 key,跳过一定没有出现 key 的 sstable**


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/FilterPolicy_UML_10_03.png)   
![](assets/BloomFilterPolicy_10_03.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- `CreateFilter()`:接受一组排序的 key,生成一个过滤器(append 到 `dst`)
- `KeyMayMatch()`:如果 key 在传递函数 `CreateFilter()` 的 key 列表中,则必须返回 true;注意,它不需要精确,也就是即使 key 不在前面传递的 key 列表中,也可以返回 true

- `NewBloomFilterPolicy()`:工厂函数,返回一个默认的布隆过滤器


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

默认实现是 [BloomFilter]()


&nbsp;   
<a id="reference"></a>
## 参考资料

- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb - filter_policy](https://dirtysalt.github.io/html/leveldb.html#org88fac7d)

================================================
FILE: architecture/SSTable/Iterator - 2018-10-01 - rsy.md
================================================
# Iterator - 2018-10-01 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`include/leveldb/iterator.h`, `table/iterator.cc`

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

leveldb 中对 key 的查找和遍历,上层统一使用 `Iterator` 的方式处理,屏蔽底层的处理,统一逻辑。 提供 `RegisterCleanup()` 可以在 `Iterator` 销毁时,做一些清理工作(比如释放 `Iterator` 持有句柄的引用)


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/Iterator_base_UML_10_01.png)

leveldb 中 key 的查找遍历, 存储层面之上统一通过 Iterator 的方式处理。

存储结构(memtable/sstable/block) 都提供对应的 Iterator,另外还有为操作方便封装的特殊 Iterator。

- [`MemTableIterator`]() (`db/memtable.cc`):memtable 对 key 的查找和遍历封装成 `MemTableIterator`。 底层直接使用 SkipList 的类  Iterator 接口
- [`TwoLevelIterator`]() (`table/two_level_iterator.cc`):SSTable 对于类似 index ==> data 这种需要定位 index,然后根据 index 定位到具体 data 的使用方式
- [`Block::Iter`]() (`table/block.cc`):上层对 Block 进行 key 的查找和遍历,封装成 `Block::Iter` 处理
- [`LevelFileNumIterator`]() (非 level-0 的 sstable 元信息集合的 Iterator) (`db/version_set.cc`):level-0 中的 sstable 可能存在 overlap,处理时每个 sstable 单独处理即可
- [`IteratorWrapper`]() (`table/iterator_wrapper.h`):提供了稍作优化的 Iterator 包装, 它会保存每次 `Key() / Valid()`的值, 从而避免每次调用 Iterator 接口产生的 virtural function 调用。另外,若频繁调用时,直接使用
保存的值,比每次计算能有更好的 cpu cache locality
- [`MergingIterator`]() (`table/merge.cc`):`MergingIterator` 内部包含多个 Iterator 的集合,(`children_`),每个操作,对 `children_` 中每个 Iterator 做同样操作之后按逻辑取边界的值即可
- [`DBIter`]() (`db/db_iter.cc`):对 db 遍历


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

- 前向遍历
- 后向遍历
- 查找
- 注册 `cleanup`


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`cleanup_` 组成一个链表,里面是回调函数,用于析构时释放资源。   
>搞不懂这块是谁写的,链表的加入方式是:   
>第一个就是头,之后来的倒序放置。

还提供了一个 `EmptyIterator`,用于在某些情况下边界值为0时的返回。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码解析22. Iterator的设计](https://zhuanlan.zhihu.com/p/45602134)
- [leveldb](https://dirtysalt.github.io/html/leveldb.html#orgfa1dda5)
- [LevelDB源码分析--使用Iterator简化代码设计](https://www.cnblogs.com/KevinT/p/3823240.html)

================================================
FILE: architecture/SSTable/Iterator_Wrapper - 2018-10-03 - rsy.md
================================================
# IteratorWrapper - 2018-10-03 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [相关依赖说明](#dependency_specification)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`table/iterator_wrapper.h`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

对于 Valid, Key 进行了缓存,管理 `Iterator` 对象的管理。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/Iterator_Wrapper_UML_10_03.png)


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb IteratorWrapper](https://dirtysalt.github.io/html/leveldb.html#org4289be2)

================================================
FILE: architecture/SSTable/LSM - 2018-10-06 - rsy.md
================================================
# LSM - 2018-10-06 rsy

- [模块概要](#module_in_brief)
- [参考资料](#reference)


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/LSM_Btree_sequential_insert_10_06.png)

B树在插入的时候,如果是最后一个node,那么速度非常快,因为是顺序写。

![](assets/LSM_Btree_random_insert_10_06.png)

但如果有更新插入删除等综合写入,最后因为需要循环利用磁盘块,所以会出现较多的随机I/O。大量时间消耗在磁盘寻道时间上。

![](assets/LSM_Btree_ranged_query_10_06.png)

解决思路:

- 放弃部分读性能,使用更加面向顺序写的树的结构来提升写性能。
  - COLA (Cache-Oblivious Look ahead Array)(e.g. tokuDB)
  - LSM tree (Log-structured merge Tree)(e.g. levelDB, HBase, Cassandra)


&nbsp;   


LSM:放弃磁盘读性能来换取写的顺序性。   
为什么用读换写:

- 内存的速度远超磁盘,1000倍以上。而读取的性能提升,主要还是依靠内存命中率而非磁盘读的次数
- 写入不占用磁盘的io,读取就能获取更长时间的磁盘io使用权,从而也可以提升读取效率

所以读性能受影响较小,而写性能则会获得较大幅度的提升,基本上是 5~10 倍左右。


&nbsp;   


LSM的思想,在于对数据的修改增量保持在内存中,达到指定的限制后将这些修改操作批量写入到磁盘中,相比较于写入操作的高性能,读取需要合并内存中最近修改的操作和磁盘中历史的数据,即需要先看是否在内存中,若没有命中,还要访问磁盘文件。

将 随机写 改为 顺序写,大大提高了 I/O 速度。   
核心思想是:

- 对变更进行批量 & 延时处理
- 通过归并排序将更新迁移到硬盘上

文件是不可修改的,他们永远不会被更新,新的更新操作只会写到新的文件中。通过周期性的合并这些文件来减少文件个数。   

但是读操作会变的越来越慢随着 sstable 的个数增加,因为每一个 sstable 都要被检查。最基本的的方法就是页缓存(也就是 leveldb 的 TableCache,将 sstable 按照 LRU 缓存在内存中)在内存中,减少二分查找的消耗。即使有每个文件的索引,随着文件个数增多,读操作仍然很慢。通过周期的合并文件,来保持文件的个数,因此读操作的性能在可接收的范围内。即便有了合 并操作,读操作仍然会访问大量的文件,大部分的实现通过布隆过滤器来避免大量的读文件操作,布隆过滤器是一种高效的方法来判断一个 sstable 中是否包 含一个特定的 key。

我们交换了读和写的随机 I/O。这种折衷很有意义,我们可以通过软件实现的技巧像布隆过滤器或者硬件(大文件 cache)来优化读性能。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [Log Structured Merge Trees(LSM) 原理](http://www.open-open.com/lib/view/open1424916275249.html)
- [LSM 算法的原理是什么? - 知乎](https://www.zhihu.com/question/19887265)
- [B+树 LSM 树 COLA树 原理及在海量存储中的应用](https://blog.csdn.net/anderscloud/article/details/7181085)
- [数据库如何抵抗随机IO:问题、方法与现实](http://wangyuanzju.blog.163.com/blog/static/13029201132154010987)
- [日志结构的合并树 The Log-Structured Merge-Tree](https://www.cnblogs.com/siegfang/archive/2013/01/12/lsm-tree.html)

================================================
FILE: architecture/SSTable/MergingIterator - 2018-10-05 - rsy.md
================================================
# MergingIterator - 2018-10-05 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`table/merge.h`, `table/merge.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/MergingIterator_children_10_05.png)

长得像 `TwoLevelIterator`,但是没有上层的管理,于是相当于是 **多路 Iterator 归并称为一个 Iterator 进行遍历**,用于 `DBImpl::NewInternalIterator()` 中收集所有 iterator(memtable, imm memtable, sstable)然后统一处理。

迭代器的遍历过程就是不断寻找所有子容器当前迭代器所指向的 key 最小的迭代器的过程。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/MergingIterator_UML_10_05.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

工厂接口 `Iterator* NewMergingIterator()`:接受 n 个 iterator,返回一个 iterator 用于遍历。


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

函数 `DBImpl::NewInternalIterator()` 有一些处理逻辑,就是收集所有能用到的 iterator,生产一个`MergingIterator`。这包括 MemTable,Immutable MemTable,以及各 sstable。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`MergingIterator` 直接托管了资源。

- `MergingIterator::Seek()`:每个 iterator 都 seek,然后调用 `FindSmallest()`
- `MergingIterator::Next()`:首先调整其他 iterator 都比 `current_` 大,然后 `current_` 前进一步,再调用 `FindSmallest()`
- `MergingIterator::Prev()`:同上
- `MergingIterator::FindSmallest()`:设置 `current_` 为当前所有 iterator 中值最小的那个
- `MergingIterator::FindLargest()`:同上


&nbsp;   
<a id="reference"></a>
## 参考资料

- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [leveldb MergingIterator](https://dirtysalt.github.io/html/leveldb.html#orgb23a3d9)
- [LevelDB源码解析23. Merge Iterator](https://zhuanlan.zhihu.com/p/45661955)
- [leveldb源码剖析---迭代器设计](https://blog.csdn.net/Swartz2015/article/details/71404211)

================================================
FILE: architecture/SSTable/README.md
================================================
- [FilterBlock](#filterblock)
- [Block](#block)
- [Table](#table)
- [MergingIterator](#mergingiterator)
- [TwoLevelIterator](#twoleveliterator)
- [Filter(meta) Block](#filter_meta_block)
- [Compaction](#compaction)
- [LSM](#lsm)


&nbsp;   
<a id="filterblock"></a>
## [FilterBlock](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/FilterBlock%20-%202018-10-03%20-%20rsy.md)
![](assets/FilterBlock_structure_10_03.png)

`filter block` 就是 `meta block`


&nbsp;   
<a id="block"></a>
## [Block](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Block%20-%202018-10-02%20-%20rsy.md)
![](assets/data_block_record_11_12.png)
![](assets/Block_data_structure_10_02.png)


&nbsp;   
<a id="table"></a>
## [Table](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Table%20-%202018-10-04%20-%20rsy.md)

![](assets/leveldb_sstable_structure_10_03.png)


- **Data Block** 内的 KV 记录是按照 Key 由小到大排列的
- **Index Block** 的每条记录是对某个Data Block建立的索引信息,每条索引信息包含三个内容:
  - Data Block 中 key 上限值(不一定是最大key)
  - Data Block 在 .sst 文件的偏移和大小


>(转)data index block 组织形式和 data block 非常类似,只不过有两个不同。1) data index block 从不刷新直到 Table 构造完成之后才会刷新,所以 对于一个 table 而言的话只有一个 data index block。2) data index block 添加的 k-v 是在 data block 形成的时候添加的,添加 key 非常取巧 ,是上一个data block和这个 data block 的一个 key seperator。比如上一个 data block 的 max key 是 "abcd",而这个 data block 的 min key 是 "ad"。那么这个 seperator 可以设置成为 "ac"。seperator 的生成可以参考 Comparator。使用尽量短的 seperator 可以减小磁盘开销并且提高效率。而对于添加的 value 就是这个 data block 的 offset.同样在 data index block 也会存在 restart point。   
>然后看看进行一个 key 的 query 是如何进行的。首先读取出 data index block(这个部分可以常驻内存),得到里面的 restart point 部分。针对 restart point 进行二分。因为 restart point 指向的 key 都是全量的 key。如果确定在某两个 restart point 之间之后,就可以遍历这个 restart point 之间范围分析 seperator。得到想要查找的 seperator 之后对应的 value 就是某个 data block offset。读取这个 data block 和之前的方法一样就可以查找 key 了。对于遍历来说,过程是一样的。   
>这里我们稍微分析一下这样的工作方式的优缺点。对于写或者是 merge 来说的话,效率相当的高,所有写都是顺序写并且还可以进行压缩。影响写效率的话一个重要参数就是 flush block 的参数。 但是对于读来说的话,个人觉得过程有点麻烦,但是可以实现得高效率。对于 flush block 调节会影响到 data index block 和 data block 占用内存大小。如果 flush block 过大的话, 那么会造成 data index block 耗费内存小,但是每次读取出一个 data block 内存很大。如果 flush block 过小的话,那么 data index block 耗费内存很大,但是每次读取 data block 内存很小。 而 restart point 数量会影响过多的话,那么可能会占用稍微大一些的内存空间,但是会使得查找过程更快(遍历数更少)。   

### SSTable 的访问
![](assets/SSTable_access_10_07.jpg)


### SSTable 的建立
![](assets/SSTable_build_10_07.jpg)


### SSTable 的内部格式
![](assets/SSTable_format_10_07.jpg)


&nbsp;   
<a id="mergingiterator"></a>
## [MergingIterator](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/MergingIterator%20-%202018-10-05%20-%20rsy.md)

![](assets/MergingIterator_children_10_05.png)

多路 Iterator 归并称为一个 Iterator 进行遍历,迭代器的遍历过程就是不断寻找所有子容器当前迭代器所指向的 key 最小的迭代器的过程。

用于 `DBImpl::NewInternalIterator()` 中收集所有 iterator(memtable, imm memtable, sstable)然后统一处理。


&nbsp;   
<a id="twoleveliterator"></a>
## [TwoLevelIterator](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/TwoLevelIterator%20-%202018-10-03%20-%20rsy.md)

将对 Table 的遍历封装,对外展现如同线性遍历。


&nbsp;   
<a id="filter_meta_block"></a>
## [Filter(meta) Block](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/FilterBlock%20-%202018-10-03%20-%20rsy.md)

**`filter block` 就是 `meta block`。**    
`filter block` 存储的是 `data block` 数据的一些过滤信息,用于加快查询的速度。

![](assets/Filter_Block_structure_10_03.png)


&nbsp;   
<a id="compaction"></a>
## [Compaction](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Compaction%20-%202018-10-05%20-%20rsy.md)

- LevelDB 存储分为两部分,一部分在内存,另一部分在磁盘上。内存中方便快速查找, 查找失败后,去磁盘上查找。一段时间后或内存达到一定大小,会将内存中的 compact 成 .sst 文件存在磁盘
- compaction 是执行 LSM-tree 中 merge 的过程
- 删除操作在 memtable 只是打上删除标记,**真正的删除** 在 compaction 中做
- **minor compaction** 用于内存到外存的迁移过程。level-0 是将 memTable 整个 dump 出来的结果,因此可能之间有相交,这个操作叫 minor compaction
- **major compaction** 用于 level 之间的迁移。major compaction 是将 层i 合到 层i+1 中去,合并是将 层i 中的一块合到整个 层i+1 中去, 这个过程类似归并排序,将参与的几个块全部取出来排序,再重新组合成新的 层i+1,同时 会将重复的 key 弃掉(弃掉是指如果 key 已经出现在更低的层,则高级的层不需要记录这个 key)
  - 对 level > 0 的 sstables,选择其中一个 sstable 与 下一层 sstables 做合并
  - 对 level-0 的 sstables,在选择一个 sstable 后,还需要找出所有与这个 sstable 有 key 范围重叠的 sstables,最后统统与 level-1 的 sstables 做合并

### Minor Compation

![](assets/Minor_Compaction_10_05.png) 

### Major Compaction

![](assets/Major_Compaction_10_05.png)


&nbsp;   
<a id="lsm"></a>
## [LSM](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/LSM%20-%202018-10-06%20-%20rsy.md)

![](assets/LSM_Btree_sequential_insert_10_06.png)

B树在插入的时候,如果是最后一个node,那么速度非常快,因为是顺序写。

![](assets/LSM_Btree_random_insert_10_06.png)

但如果有更新插入删除等综合写入,最后因为需要循环利用磁盘块,所以会出现较多的随机I/O。大量时间消耗在磁盘寻道时间上。

![](assets/LSM_Btree_ranged_query_10_06.png)

将 随机写 改为 顺序写,大大提高了 I/O 速度。   
核心思想是:

- 对变更进行批量 & 延时处理
- 通过归并排序将更新迁移到硬盘上

文件是不可修改的,他们永远不会被更新,新的更新操作只会写到新的文件中。通过周期性的合并这些文件来减少文件个数。   

但是读操作会变的越来越慢随着 sstable 的个数增加,因为每一个 sstable 都要被检查。最基本的的方法就是页缓存(也就是 leveldb 的 TableCache,将 sstable 按照 LRU 缓存在内存中)在内存中,减少二分查找的消耗。即使有每个文件的索引,随着文件个数增多,读操作仍然很慢。通过周期的合并文件,来保持文件的个数,因此读操作的性能在可接收的范围内。即便有了合 并操作,读操作仍然会访问大量的文件,大部分的实现通过布隆过滤器来避免大量的读文件操作,布隆过滤器是一种高效的方法来判断一个 sstable 中是否包 含一个特定的 key。

我们交换了读和写的随机 I/O。这种折衷很有意义,我们可以通过软件实现的技巧像布隆过滤器或者硬件(大文件 cache)来优化读性能。

================================================
FILE: architecture/SSTable/Table - 2018-10-04 - rsy.md
================================================
# Table - 2018-10-02 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`include/leveldb/table.h`, `table/table.cc`, `table/table_builder.cc`, `include/leveldb/table_builder.h`

`SSTable` 文件格式:   
![](assets/SSTable_structure_10_03.png)

- **Data Block** 内的 KV 记录是按照 Key 由小到大排列的
- **Index Block** 的每条记录是对某个Data Block建立的索引信息,每条索引信息包含三个内容:
  - Data Block 中 key 上限值(不一定是最大key)
  - Data Block 在 .sst 文件的偏移和大小


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/Table_UML_10_02.png)

![](assets/TableBuilder_10_03.png)


&nbsp;   
<a id="module_function"></a>
## 模块功能

- `Table` 类主要完成 sstable 的读取逻辑
- `TableBuilder` 类用于构建 sstable,将 `data block`, `filter block`, `meta index block`,  `index block`, `footer` 写入文件


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

**`Table`**:

- `Table::Open()`:反序列化文件数据,`footer`, `index_block`,之后 `ReadMeta()`, `ReadFilter()`
- `Table::NewIterator()`:返回一个 `TwoLevelIterator`

**`TableBuilder`**:

- `TableBuilder::Add()`:向当前 sstable 添加 k-v,要求添加 key 的顺序必须是递增的
- `TableBuilder::Flush()`:将当前缓存的 k-v 全部 flush 到文件中
- `TableBuilder::Abandon()`:结束表的构建,并丢弃当前缓存的内容,该方法被调用后,将不再会使用传入的 `WritableFile`
- `TableBuilder::Finish()`:结束表的构建,该方法被调用后,将不再会使用传入的 `WritableFile`

一旦 `Finish()/Abandon()` 方法被调用,将不能再次执行 `Flush()` 或者 `Add()` 操作。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

- `Table::BlockReader()`:解析出一个 `Block`,然后维护一下 `Cache`
- `Table::InternalGet()`:调用 `db/version_set.cc` 中的 `SaveValue`,如果 key match 了,就把 `value` 存下来
- `TableBuilder::Finish()`:
  - 首先调用 `Flush()`,写完最后的 block
  - 然后依次写 `meta block(filter block)`, `index block`, `footer`
- `TableBuilder::WriteBlock()`:在 Flush 文件时,会调用 `WriteBlock()` 函数将 data block 写入到文件中,该函数同时还设置 data block 的 index entry 信息。该函数做些预处理工作,序列化要写入的 data block,根据需要压缩数据,真正的写入逻辑是在 `WriteRawBlock()` 函数中
- `TableBuilder::WriteRawBlock()`:把一个 block 和 结尾的 5B 写入文件,并维护 `rep_` 的 size 和 offset


&nbsp;    
&nbsp;    
`TableBuilder::Add()`:看了好一会才懂,数据的序列化和反序列化这个地方太硬核了,好多地方我都记不住,根本想不起来哪些地方存的啥东西。。。

>(转1)直到遇到下一个 databock 的第一个 key 时,我们才为上一个 datablock 生成 index entry,这样的好处是:可以为 index 使用较短的 key;比如上一个 data block 最后一个 k/v 的 key 是"the quick brown fox",其后继 data block 的第一个 key 是 "the who",我们就可以用一个较短的字符串 "the r" 作为上一个 data block 的 index block entry 的 key。

>(转2)data index block 组织形式和 data block 非常类似,只不过有两个不同。1)data index block从不刷新直到Table构造完成之后才会刷新,所以 对于一个table而言的话只有一个data index block. 2)data index block 添加的 key/value 是在 data block 形成的时候添加的,添加 key 非常取巧 ,是上一个data block和这个 data block 的一个 key seperator。比如上一个 data block 的 max key 是 "abcd",而这个 data block 的min key是 "ad"。那么这个 seperator 可以设置成为 "ac"。seperator 的生成可以参考 Comparator。使用尽量短的 seperator 可以减小磁盘开销并且提高效率。而对于添加的 value 就是这个 data block 的 offset.同样在 data index block 也会存在 restart point。   
>然后看看进行一个key的query是如何进行的。首先读取出 data index block(这个部分可以常驻内存),得到里面的restart point部分。针对 restart point 进行二分。因为restart point指向的key都是全量的key.如果确定在某两个restart point之间之后,就可以遍历这个restart point之间范围分析seperator. 得到想要查找的seperator之后对应的value就是某个data block offset.读取这个data block和之前的方法一样就可以查找key了。对于遍历来说,过程是一样的。   
>这里我们稍微分析一下这样的工作方式的优缺点。对于写或者是merge来说的话,效率相当的高,所有写都是顺序写并且还可以进行压缩。影响写效率的话一个重要参数就是flush block的参数。 但是对于读来说的话,个人觉得过程有点麻烦,但是可以实现得高效率。对于flush block调节会影响到data index block和data block占用内存大小。如果flush block过大的话, 那么会造成data index block耗费内存小,但是每次读取出一个data block内存很大。如果flush block过小的话,那么data index block耗费内存很大,但是每次读取data block内存很小。 而restart point数量会影响过多的话,那么可能会占用稍微大一些的内存空间,但是会使得查找过程更快(遍历数更少)。   


&nbsp;   
<a id="reference"></a>
## 参考资料

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [leveldb-handlbook sstable](https://leveldb-handbook.readthedocs.io/zh/latest/sstable.html)
- [levelDB SSTable-静态布局结构](https://www.cnblogs.com/shenzhaohai1989/p/3907373.html)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb - Table](https://dirtysalt.github.io/html/leveldb.html#orgb0dff4a)
- [LevelDB 5.SSTable](https://www.cnblogs.com/ym65536/p/7751229.html)
- [LevelDB源码解析16.SST文件格式](https://zhuanlan.zhihu.com/p/44954881)
- [LevelDB源码解析24. SST文件生成器](https://zhuanlan.zhihu.com/p/45693221)
- [LevelDB源码解析25. SST文件内存表示](https://zhuanlan.zhihu.com/p/45730480)
- [leveldb源码分析之写sst文件](http://luodw.cc/2015/10/21/leveldb-10/)
- [leveldb源码分析之读sst文件](http://luodw.cc/2015/10/24/leveldb-12/)
- [SSTable之文件格式及生成-leveldb源码剖析(7)](http://www.pandademo.com/2016/04/sstable-file-format-and-build-leveldb-source-dissect-7/)
- [leveldb - sstable格式](https://www.cnblogs.com/cobbliu/p/6194072.html)
- [leveldb源码分析--SSTable之TableBuilder](https://www.cnblogs.com/KevinT/p/3816746.html)

================================================
FILE: architecture/SSTable/TwoLevelIterator - 2018-10-03 - rsy.md
================================================
# TwoLevelIterator - 2018-10-03 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`table/two_level_iterator.h`, `table/two_level_iterator.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

![](assets/TwoLevelIterator_11_15.jpg)

将对 `Table` 的遍历封装,对外展现如同线性遍历。


&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/TwoLevelIterator_UML_10_03.png)


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

工厂函数:   `Iterator* NewTwoLevelIterator()`


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

两层迭代器都是 `IteratorWrapper` 类型而不是 iter,主要是为了缓存 key 和 valid。

`DBImpl::NewIterator()` -> `DBImpl::NewInternalIterator()` -> `Version::AddIterators()` -> `TableCache::NewIterator()` -> `Table::NewIterator()` -> `NewTwoLevelIterator()`


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

`BlockFunction` 用于在遍历不同的 block 时,解析不同的 k-v。

- `SkipEmptyDataBlocksForward()` 封装了 “如果应该到下一个 block,就进入下一个 block 的第一个”,把其他函数从 “边界处理” 上解放出来。
- `SkipEmptyDataBlocksBackward()` 同上


&nbsp;   
<a id="reference"></a>
## 参考资料

- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码解析26. 二级迭代器](https://zhuanlan.zhihu.com/p/45829937)
- [LevelDB源码分析之十四:TwoLevelIterator](https://blog.csdn.net/caoshangpa/article/details/79046942)
- [leveldb TwoLevelIterator](https://dirtysalt.github.io/html/leveldb.html#org46fb0c0)
- [LevelDB源码分析--使用Iterator简化代码设计](https://www.cnblogs.com/KevinT/p/3823240.html)

================================================
FILE: architecture/util/Arena/arena - 2018-09-30 - rsy.md
================================================
# arena - 2018-09-27 rsy

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`util/arena.h`, `util/arena.cc`


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

用于分配空间,不含有GC & RC.

leveldb实现了定制的Arena内存分配器,并没有直接使用glibc的malloc或者c++标准库的new。

Arena 主要与 MemTable 关联使用,实际主要用于 SkipList 中的 Node 内存分配,统一 MemTable 的内存分配需求,减少内存分配的实际系统调用次数(尤其针对小块内存),减少内存分配中的空洞(碎片),但也会造成一定的内存浪费;统一内存释放,不必频繁 new/delete;鉴于 Arena 在 leveldb 中的使用场景不需考虑线程安全。Arena 的实现简单轻量,代码总计百余行,服务于 leveldb 的定制需求,提高应用性能,并且提供了内存对齐的版本。

&nbsp;   
<a id="module_function"></a>
## 模块功能

![](assets/Arena_UML_09_27.png)

&nbsp;   
<a id="interface_specification"></a>
## 接口说明

分配所需大小的(对齐)空间。


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

被 `memtable` private 含有,用于在 `memtable` 中分配空间,在 `memtable` 析构时 `arena` 析构。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

![](assets/Arena_alloc_09_29.png)

如果要申请的空间小于当前这一条的剩余,那么ok。否则:1. 若申请空间大于 1M,单独开一个申请大小的条;2. 开一个4M的条,并维护当前指针和剩余空间大小。   
没有 GC 和 RC,在析构时(在 `memtable` 中析构)统一全部释放。


&nbsp;   
<a id="reference"></a>
## 参考资料

- [和我一起学习leveldb 7 util(续)](http://brg-liuwei.github.io/tech/2014/11/17/leveldb-7.html)
- [Arena内存管理-leveldb源码剖析(1)](http://www.pandademo.com/2016/03/arena-memory-management-leveldb-source-dissect-1/)
- [LevelDB源码剖析之Arena内存管理](http://mingxinglai.com/cn/2013/01/leveldb-arena/)
- [leveldb - Arena](https://dirtysalt.github.io/html/leveldb.html#orgfda6998)
- [leveldb源码分析之内存池Arena](http://luodw.cc/2015/10/15/leveldb-04/)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)

================================================
FILE: architecture/util/BloomFilter/BloomFilter - 2018-10-10 mk.md
================================================
# BloomFilter - 2018-10-10 毛恺

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

BloomFilter(util/bloom.cc)

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要
leveldb采用分层储存结构,查询时最差的结果是在所有的层(level)上都查询一次。

Bloom Filter是一种空间效率很高的随机数据结构,可以使用较少的空间存储大量数据的全集,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。

这样做的代价是:再判断一个元素是否属于一个集合时,有可能会错把其他元素误以为是该集合元素。
当追求“零错误”时,BloomFilter不适用。

&nbsp;   
<a id="module_function"></a>
## 模块功能
![](assets/BloomFilterPolicy.png)

CreateFilter(const Slice* keys, int n, std::string* dst)
将传入的n个key 存储到bloomfilter 中,bloomfilter结果使用string存储。

KeyMayMatch(const Slice* keys,const Slice& bloom_filter)
判断一个 key 在bloomfilter中是否存在。

BloomFilterPolicy(int bits_per_key)
按照公式设定hash函数的个数

&nbsp;   
<a id="interface_specification"></a>
## 接口说明

构造函数:
explicit BloomFilterPolicy(int bits_per_key)
没有默认构造函数,使用时必须传入bits_per_key,表示每个key的大小


&nbsp;   
<a id="reference"></a>
## 参考资料

Bloom Filter概念和原理:https://blog.csdn.net/jiaomeng/article/details/1495500

================================================
FILE: architecture/util/Coding/coding - 2018-09-06 - rsy.md
================================================
# coding - 2018-09-26 rsy

- [模块信息](#module_info)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

`util/coding.h`, `util/coding.cc`    
Coding 主要是用来完成变长数据以及字符串还有Slice的序列化和反序列化的。
主要用途是用来 **省空间**。


&nbsp;   
<a id="module_function"></a>
## 模块功能

- PutFixed32 写入定长uint32
- PutFixed64 写入定长uint64
- PutVarint32 写入变长uint32
- PutVarint64 写入变长uint64
- PutLengthPrefixedSlice 加入长度前置的slice,即先加长度,再加string.
- GetVarint32 读取变长uint32,并删掉这个数字。
- GetVarint64 读取变长uint64,并删掉这个数字。
- GetLengthPrefixedSlice 读取数字后的数据到 result,并删掉 input开头的数字。(数据格式:`len(varint32), data[len](uint8)`)
- GetVarint32Ptr(p,limit,v) 将[p,limit)部分的内存解析为变长uint32放到v里面,返回下一个字节
- GetVarint64Ptr(p,limit,v) 将[p,limit)部分的内存解析为变长uint64放到v里面,返回下一个字节
- VarintLength 变长uint32/uint64长度
- EncodeFixed32 PutFixed32 保证小端序
- EncodeFixed64 PutFixed64
- EncodeVarint32 PutVarint32
- EncodeVarint64 PutVarint64
- DecodeFixed32 读取定长uint32
- DecodeFixed64 读取定长uint64
- GetVarint32Ptr 从[p,limit)读取uint32并且返回下一个字节.


&nbsp;   
<a id="interface_specification"></a>
## 接口说明

使用 编解码 主要是因为 涉及到各种 key 是 Varint 类型。   
编解码均分为 fixed 和 var 两种。   

主要用于 压缩 int的存储空间,最终存为 string,然后用的时候再 decode。


&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

- 小端序 little-ending
- 每个 byte 只利用 **低7位**,**最高位存储是否还有后续的部分**。所以对于大数,有可能用 5个 bytes。(真是细节到爆炸。。。)
- `GetVarint32Ptr` 用于 Varint32 解码,该函数处理 只有1byte的情况,否则转发给 `GetVarint32PtrFallback`。在 `GetVarint32Ptr` 和 `GetVarint32PtrFallback` 函数中,参数 `p` 是指向一个包含 varint 的字符串,`limit` 在调用的时候都是赋值为 `limit = p + 5`, 这是因为 varint 最多占用5个字节。`value` 用于存储返回的int值。

![](assets/Coding_09-27.png)

&nbsp;   
<a id="reference"></a>
## 参考资料

- [LevelDB源码剖析之Varint](http://mingxinglai.com/cn/2013/01/leveldb-varint32/)
- [leveldb - Coding](https://dirtysalt.github.io/html/leveldb.html#orgad70de3)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)

================================================
FILE: architecture/util/Comparator/Comparator - 2018-10-10 - mk.md
================================================
# Comparator - 2018-10-10 毛恺

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [相关依赖说明](#dependency_specification)

&nbsp;   
<a id="module_info"></a>
## 模块信息
Comparator(util/comparator.cc,include/leveldb/comparator.h,db/dbformat.h,db/dbformat.cc)


&nbsp;   
<a id="module_in_brief"></a>
## 模块概要
Comparator:
leveldb是按Key序组织的,对于Key的比较非常重要。
Comparator的主要作用就是Key的比较,同时也会对key进行一定的优化,以降低一些存储空间。

BytewiseComparatorImpl&InternalKeyComparator:
在dbformat中实现的两种Comparator。

&nbsp;   
<a id="module_function"></a>
## 模块功能
![](assets/Comparator.png)

在comparator.h中定义,主要函数有:
Compare 为实际使用的比较函数。
Name 获取comparator的名称,主要为了防止建立和读取时使用了不同的Comparator。
FindShortestSeparator 找出参数[start,limit)区间内间一个短的串,主要作用是降低一点储存空间。
FindShortestSuccessor 作用类似上一个函数。

![](assets/BytewiseComparatorImpl.png)

在comparator.cc中实现。
其Compare函数直接调用Slice的Compare。

![](assets/InternalKeyComparator.png)


&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明

需要定义 "db/dbformat.h" "include/leveldb/comparator.h"

================================================
FILE: architecture/util/Env_Options/Env - 2018-10-04 -jyh.md
================================================
# Env - 2018-10-04 季宇恒

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)

&nbsp;   
<a id="module_info"></a>
## 模块信息
util/env.cc

include/leveldb/env.h

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要
考虑到移植以及灵活性,leveldb将系统相关的处理(文件/进程/时间之类)抽象成Env,用户可以自己实现相应的接口,作为Option传入。默认使用自带的实现。
<a id="module_function"></a>
## 模块功能

返回一个适合当前操作的默认环境系统。成熟的用户可能希望提供自己的环境实现而不是依赖于这个默认环境。Default()的结果属于leveldb,永远不能删除。
用指定的名称创建一个全新的顺序可读文件。成功后,在*result中存储指向新文件的指针并返回OK。失败时在*result中存储NULL并返回non。如果文件不存在,返回非ok状态。返回的文件一次只能被一个线程访问。
创建一个全新的随机访问只读文件指定名称。成功时,将指向新文件的指针存储进去*结果并返回OK。在失败中,在*result和中存储NULL返回non-OK。如果文件不存在,返回一个非ok状态。返回的文件可能被多个线程并发访问。
创建一个用指定的对象写入新文件名字。删除任何同名的现有文件,并创建一个新文件。成功时,将指向新文件的指针存储进去*结果并返回OK。在失败中,在*result和中存储NULL返回non-OK。返回的文件一次只能被一个线程访问。
在*中存储指定目录的子目录的名称。这些名称与“dir”相关。删除*结果的原始内容。
锁定指定的文件。用于防止并发访问同一个数据库由多个进程组成。失败时,存储NULL*锁定并返回非ok。在成功时,存储一个指向表示的对象的指针在*lock中获取锁,返回OK。打电话的人应该打电话解锁文件(*锁)以释放锁。如果进程退出,锁会自动释放。如果别人已经锁上了,马上锁上失败了。此调用不等待现有锁走开。
在后台线程中运行一次“(*函数)(arg)”。“函数”可以在未指定的线程中运行。多种功能添加到同一个环境中,可以在不同的线程中并发运行。调用者可能不认为后台工作项是序列化。
启动一个新线程,在新线程中调用“function(arg)”。当“function(arg)”返回时,线程将被销毁。
*path设置为一个可以用于测试的临时目录。它可能或者许多东西不是刚刚创造出来的。目录可能不同,也可能不同在同一进程运行之间,但后续调用将返回相同的目录中。

&nbsp;   
<a id="interface_specification"></a>
## 接口说明

leveldb::Env

leveldb::FileLock

leveldb::Logger

leveldb::RandomAccessFile

leveldb::SequentialFile

leveldb::WritableFile

&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明
需要Export类型的定义和用法
Export (include/leveldb/export.h)
需要Status类型的定义和用法
Export (include/leveldb/status.h)

操作需要返回值

&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

&nbsp;   
<a id="reference"></a>
## 参考资料
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)


================================================
FILE: architecture/util/Env_Options/Options - 2018-10-04 - jyh.md
================================================
# Options - 2018-10-04 季宇恒

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

util/options.cc

include/leveldb/options.h

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要
leveldb中启动时的一些配置通过Option传入,get/put/delete时,也有相应的ReadOption/WriteOption

&nbsp;   
<a id="module_function"></a>
## 模块功能

控制传入的comparator,open时如果db目录不存在就创建,存在就报错
控制是否保存中间的错误状态,用compact时是否读到的block做检验
控制传入的env,打印日志的logger,memtable的最大size
控制db中打开文件的最大个数,db中需要打开的文件包括基本的CURRENT/LOG/MANIFEST/LOCK,以及打开的sstable文件
控制传入block数据的cache管理
控制对size的管理
block中对key做前缀压缩

&nbsp;   
<a id="interface_specification"></a>
## 接口说明

1.leveldb::Options
传入comparator,open时如果db目录不存在就创建,存在就报错
sstable一旦打开就会将index信息加入TableCache,所以把(max_open_files-10)作为tablecache的最大数量

2.leveldb::ReadOptions
是否对读到的block做检验,读到的block是否加入block cache,指定读取的SnapShot

3.leveldb::WriteOptions
write时,记binlog之后,是否对binlog做sync,如果传入不为NULL,write完成之后同时做SnapShot

&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明
需要Export类型的定义和用法
Export (include/leveldb/export.h)
操作需要返回值

&nbsp;   
<a id="reference"></a>
## 参考资料
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)


================================================
FILE: architecture/util/Env_Options/README.md
================================================


================================================
FILE: architecture/util/LRU/cache - 2018-09-20 - rsy.md
================================================
# Cache - 2018-09-20 - rsy

- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


<a id="module_in_brief"></a>
## 模块概要
cache.h (include/leveldb/cache.h)   
cache.cc (util/cache.cc)

Cache主要是用来作为kv查询cache部分,主要用于 [`TableCache`](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/TableCache%20-%202018-09-30%20-%20rsy.md) 和 [`BlockCache`]() 中。

`TableCache` 的 KV 格式:

- 以 `file_number` 作 key
- 以 `TableAndFile` 对应的预加载索引作为 value。

`BlockCache` 的 KV 格式:

- 以 `cache_id` 和该数据块的 `offset`(通过传入的数据块索引可知)联合作key
- 以包含 `BlockContents` 的 `Block` 作为value


&nbsp;    
<a id="module_function"></a>
## 模块功能

![](assets/cache_09_20.png)
![](assets/SharededLRUCache_09_20.png)

Cache 功能:(保证线程安全)

- Insert // 插入kv返回Handle
- Lookup // 查询k返回Handle
- Value // 传入handle得到对应的value
- Erase // 删除kv
- NewId // 产生一个新id

`ShardedLRUCache`,所谓 Shard 意思非常简单,就是将所有的请求进行 load-balance。将 `LRUCache` 再做 shard 的目的是为了加速查找和减少冲突。   
整体来看,上层使用 cache 时,首先根据 key 做 shard,然后在 `LRUCache` 层对 `HashHandle` 做数据的操作,最后处理 lru 逻辑。

&nbsp;   
leveldb 利用上述的 cache 结构来缓存数据。其中:

- cache:来缓存已经被打开的 sstable 文件句柄以及元数据(默认上限为500个)
- bcache:来缓存被读过的 sstable 中 dataBlock 的数据(默认上限为8MB)

当一个 sstable 文件需要被打开时,首先从 cache 中寻找是否已经存在相关的文件句柄,若存在则无需重复打开;若不存在,则从打开相关文件,并将(1)indexBlock 数据,(2)metaIndexBlock 数据等相关元数据进行预读。


&nbsp;    
<a id="interface_specification"></a>
## 接口说明

对外接口主要是:   
创建工厂:

    Cache* NewLRUCache(size_t capacity) {
        return new ShardedLRUCache(capacity);
    }
需要用户手动 `delete`.   

操作接口:同 `Cache` 接口。


&nbsp;    
<a id="dependency"></a>
## 相关依赖说明

在 [`TableCache`](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/TableCache%20-%202018-09-30%20-%20rsy.md) 中的作用:   

TableCache 用于缓存SSTable的对应索引元数据。   
TableCache的查找流程,用户提交key查询,交由 `Status DBImpl::Get(const ReadOptions& options, const Slice& key, std::string* value)` ,获取两种 `MemTable` 和当前 `Version`,依次查询 memtable、 immutable memtable,未找到则在当前 `Version `上 `Status Version::Get(const ReadOptions& options, const LookupKey& k, std::string* value, GetStats* stats)`,依次从 最低level 到 最高level 查询直至查到,在每层确定可能包含该 key 的 SSTable 文件后,就会在所属 `VersionSet` 的`table_cache` 中继续查询,即调用 `Status TableCache::Get(const ReadOptions& options, uint64_t file_number, uint64_t file_size, const Slice& k, void* arg, void (*handle_result)(void*, const Slice&, const Slice&))`


&nbsp;    
<a id="inner_detail"></a>
## 内部实现细节

内部有 `port::Mutex mutex_`(RAII),保证线程安全。


&nbsp;   
关于 **Handle**:   

Handle是了为了管理cache item。Handle 为空结构体,是因为用于操作的其实是 `LRUHandle`,虽然没有继承,但是 Handle 为空,所以无所谓了。(因为继承空类会被优化掉)

![](assets/LRUHandle_09_24.png)

可以看到有3个指针:

- `next_hash`:hash表中的下一个结点。(HandleTable使用链表实现,最后一个结点的next_hash是NULL)
- `next`,`prev`:在LRU双向链表中的数据结构。

在 **`TableCache`** 中:   
`char* key_data` 存储的是 `file_number`.   
`void* value` 存储的是 `file` 和 `Table`.   

所有的 cache 数据都是存储在一个双向 LRU-list 中。LRUHandle是 LRUCache 的管理对象,代表 Cache 里面的元素。LRUHandle一方面在LRU队列里面需要维护指针,一方面在hashtable(HandleTable下面会讲到)需要维护指针。同时存在于LRU队列和HashTable里面。


&nbsp;    
`LRUHandle` 中,`charge` 的作用是记录这条 cache entry 中的 `value` 所指的内存区域大小,cache 的内容有容量限制。   
`LRUHandle->refs` 表示引用计数。为 0 时,表示不在 cache 中;为 1 时,表示在 `lru_` 中;大于 1 时,在 `in_use_` 中,其值减一表示被 clients 引用的次数。   
`LRUHandle` 结构体最后定义了 `char key_data[1];`,此为C程序员惯用手法:[未知大小数组 - 柔性数组](https://zh.cppreference.com/w/c/language/array#.E6.9C.AA.E7.9F.A5.E5.A4.A7.E5.B0.8F.E6.95.B0.E7.BB.84)   
>在 struct 定义中,未知大小数组必须出现作最后一个元素(只要有一个具名成员),这种情况下,这是称为柔性数组成员的特殊情形。


&nbsp;    
**HandleTable 在 resize 时的 lock-free**:   

**下面这段和 leveldb 没关系,不过挺有意思的:网上看是这么说的:**   

-----
当cache中维护的数据量太大时,会发生哈希表扩张的情况。以下两种情况是为“cache中维护的数据量过大”:

- 整个cache中,数据项(node)的个数超过预定的阈值(默认初始状态下哈希桶的个数为16个,每个桶中可存储32个数据项,即总量的阈值为哈希桶个数乘以每个桶的容量上限)
- 当cache中出现了数据不平衡的情况。当某些桶的数据量超过了32个数据,即被视作数据发生散列不平衡。当这种不平衡累积值超过预定的阈值(128)个时,就需要进行扩张

![](assets/HandleTable_resize_09_25.png)

一次扩张的过程为:   
1. 计算新哈希表的哈希桶个数(扩大一倍)   
2. 创建一个空的哈希表,并将旧的哈希表(主要为所有哈希桶构成的数组)转换一个“过渡期”的哈希表,表中的每个哈希桶都被“冻结”。   
3. 后台利用“过渡期”哈希表中的“被冻结”的哈希桶信息对新的哈希表进行内容构建。   
当有新的读写请求发生时,若被散列之后得到的哈希桶仍然未构建完成,则“主动”进行构建,并将构建后的哈希桶填入新的哈希表中。后台进程构建到该桶时,发现已经被构建了,则无需重复构建。   
因此如上图所示,哈希表扩张结束,哈希桶的个数增加了一倍,于此同时仍然可以对外提供读写服务,仅仅需要哈希桶级别的封锁粒度就可以保证所有操作的一致性跟原子性。

- 在完成新的哈希表构建的整个过程中,哈希表并不是拒绝服务的,所有的读写操作仍然可以进行。
- 哈希表扩张过程中,最小的封锁粒度为哈希桶级别。

-----

resize 真正的实现:

发生 resize 的时机:当元素个数超过 hash 长度时。   
原因是:因为每一个 cache entry 都比较大,所以尽量让每条链最多一个元素。
>// Since each cache entry is fairly large, we aim for a small   
>// average linked list length (<= 1).

转移:遍历原桶中的所有 handle,对 key 取模(`& (new_length - 1)`),插到新的桶里。  
细节:

- `length` 永远保持 2 的幂次,这样取模的时候可以做位运算。
- 线程安全性:`HandleTable::insert()` 在元素数量超出时调用 `HandleTable::resize()`,但是不在这里上锁。锁是在 `LRUCache::insert()` 中上的。注意 `LRUCache::mutex_` 是整体级别的锁(每次锁住整个 `LRUCache`)。


&nbsp;    
**LRUCache** 与 **HandleTable**:   
![](assets/handle_table_09_24.png)   

每一个 `LRUCache` 都长这样。注意 **虚线连接的是同一个玩意** 就明白了。   
注意:并不是 hashtable 中的所有 `entry` 都有出现在 LRU-list 中(但是 所有 LRU-list 中的 node 一定在 hashtable 中有对应的 `entry`),有的 handle 被 client 持有,属于 in_use 状态。在 handle 被 client 释放后,如果还有引用计数,则重新被加入 LRU-list 头部。当 cache 中的内容被 erase 时,hashtable 和 LRU-list 中都不再出现这一元素。   
LRU 对数据结构的唯一修改就出现在 `LRUCache::Insert` 时,当内存用量超过限制时删掉 `old` 结点。(相当于在其他所有操作中都要维护 LRU 的性质,然后获得了内存使用优化的好处)   
总结:**LRU-list 和 in_use-list 的并集是 hashtable**。


**资源管理**:   
因 LRU 机制而删除一个 `LRUHandle` 时,`LRUHandle` 中资源的释放是由自身结构中的 `deleter` 函数指针完成的。这个指针是在 `Cache::insert()` 中传入的(直接保存在相应的 `LRUHandle` 中),并在 `LRUCache::Unref()` 中调用(如果引用计数为0的话)。


&nbsp;    
**ShardedLRUCache**:   
`ShardedLRUCache` 由 16个 `LRUCache` 包装成的 *hash-bucket*,hash 值取最高4位(由 `Slice` 映射到 `uint32_t`)。另外 `Capacity` 由每个 bucket 均摊。   
**为什么需要 `ShardedLRUCache`**:

- 多线程访问
- 快速
- 减少锁开销(锁是 `LRUCache::mutex_`,相当于桶级别的锁)

下面这个图不太准确:(但是主要想展示 **load-balance** 的想法)    
![](assets/SharededLRUCache_结构_09_24.png)


&nbsp;    
<a id="reference"></a>
## 参考资料

- [leveldb](https://dirtysalt.github.io/html/leveldb.html#org9904011)
- [leveldb hand-book](https://leveldb-handbook.readthedocs.io/zh/latest/cache.html#cache)
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [LevelDB源码剖析之Cache缓冲区与hash表](http://mingxinglai.com/cn/2013/01/leveldb-cache/)
- [Leveldb源码分析--11](https://blog.csdn.net/sparkliang/article/details/8740712)
- [leveldb源码分析之Table_cache](http://luodw.cc/2015/10/25/leveldb-13/)
- [leveldb源码分析之Cache](http://luodw.cc/2015/10/24/leveldb-11/)
- [SSTable之两级Cache-leveldb源码剖析(8)](http://www.pandademo.com/2016/04/two-stage-cache-of-sstable-leveldb-source-dissect-8/)

================================================
FILE: architecture/util/Logging/Logging - 2018-10-14 - jyh.md
================================================
# Logging - 2018-10-14 季宇恒

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息
util/logging.cc
util/logging.h

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要
leveldb中启动时的一些配置通过Option传入,get/put/delete时,也有相应的ReadOption/WriteOption
   
<a id="module_function"></a>
## 模块功能

将可读的“num”打印输出附加到*str
将可读的“值”打印输出附加到*str。
转义“值”中的任何不可打印字符。
返回一个人类可读的“值”版本。
转义“值”中的任何不可打印字符。
将人类可读的数字从“*in”解析为*值。成功,将“*”提前到已消费的数字,并将“*val”设置为数字值。否则,返回false并在其中留下*未指明的状态。

&nbsp;   
<a id="interface_specification"></a>
## 接口说明
Logger接口非常简单,就是Logv.允许打印变长参数.实现是PosixLogger. 

class Logger {
 public:
  Logger() { }
  virtual ~Logger();

  // Write an entry to the log file with the specified format.
  virtual void Logv(const char* format, va_list ap) = 0;

 private:
  // No copying allowed
  Logger(const Logger&);
  void operator=(const Logger&);
};

&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明
需要Slice类型的定义和用法
Slice (include/leveldb/slice.h)
需要Env类型的定义和用法
Slice (include/leveldb/env.h)

&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节

&nbsp;   
<a id="reference"></a>
## 参考资料
- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [LevelDB源码分析 - 百度文库 100多页..................](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)


================================================
FILE: architecture/util/Logging/README.md
================================================


================================================
FILE: architecture/util/README.md
================================================
# util 整合


- [Arena](#Arena)
- [Coding](#Coding)
- [ShardedLRUCache](#ShardedLRUCache)
- []()
- []()
- []()




&nbsp;   
<a id="Arena"></a>
## [Arena](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/Arena/arena%20-%202018-09-30%20-%20rsy.md)

Arena 主要与 MemTable 关联使用,实际主要用于 SkipList 中的 Node 内存分配,统一 MemTable 的内存分配需求,减少内存分配的实际系统调用次数(尤其针对小块内存),减少内存分配中的空洞(碎片),但也会造成一定的内存浪费;统一内存释放,不必频繁 new/delete;鉴于 Arena 在 leveldb 中的使用场景不需考虑线程安全。Arena 的实现简单轻量,代码总计百余行,服务于 leveldb 的定制需求,提高应用性能,并且提供了内存对齐的版本。

![](Arena/assets/Arena_alloc_09_29.png)


&nbsp;   
<a id="Coding"></a>
## [Coding](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/Coding/coding%20-%202018-09-06%20-%20rsy.md)

用于压缩 int 所占的存储空间的一套编解码工具。


&nbsp;   
<a id="ShardedLRUCache"></a>
## [ShardedLRUCache](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/LRU/cache%20-%202018-09-20%20-%20rsy.md)

**Cache 的主要流程**:   

- 查询会先在memTable中找,如果没找到,会去读index,确定在哪个block中,再去block中读, 这至少需要2次磁盘读取
- 这里的cache分两种:
    - TableCache的key为ssTable名称,值包括磁盘文件指针,以及表结构,表结构又包括 index内容及block信息。当查到某个table时,要判断这个key在不在table中,然后再去查 block
    - BlockCache为可选项,它的key是block_id,value是block内容,这样就避免了一次读取。 这种适合热点读取,如果随机读取并不适合打开BlockCache

&nbsp;   
**`ShardedLRUCache`** 是用来缓存 sstable 文件句柄以及元数据 和被读过的 sstable 中 dataBlock 的数据。

结构是 hash + LRU:

![](LRU/assets/leveldb_lrucache_09_27.png)

ShardedLRUCache 结构:由16个LRUCache构成。    
优点是:

- 快速
- 多线程
- 减少锁开销

![](LRU/assets/SharededLRUCache_结构_09_24.png)


&nbsp;   
<a id=""></a>
## []()




&nbsp;   
<a id=""></a>
## []()



&nbsp;   
<a id=""></a>
## []()









================================================
FILE: architecture/util/memenv/README.md
================================================


================================================
FILE: architecture/util/memtable/README.md
================================================


================================================
FILE: architecture/util/memtable/skiplist_2018_10_12_ss.md
================================================
# Module - 2018-10-12 苏胜

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息

SkipList(db/skiplist.h)

&nbsp;   
<a id="module_in_brief"></a>
## 模块概要

leveldb在内存中存储数据的区域称为memtable,这个memtable底层是用跳跃链表skiplist来实现的。redis也采用跳跃链表来实现有序链表。
skiplist的效率可以和平衡树媲美,平均O(logN),最坏O(N)的查询效率,但是用skiplist实现比平衡树实现简单,所以很多程序用跳跃链表来代替平衡树。
leveldb是支持多线程操作的,但是skiplist并没有使用linux下锁,信号量来实现同步控制,据说是因为锁机制导致某个线程占有资源,其他线程阻塞的情况,导致系统资源利用率降低。所以leveldb采用的是内存屏障来实现同步机制。

&nbsp;   
<a id="module_function"></a>
## 模块功能

![](SkipList_10_12.png)

skiplist类,用于操作skiplist,增加节点,比较节点等等。

![](SkipList_Node_10_12.png)

skiplist节点,可以查找下一个节点,可以设置下一个节点。

![](SkipList_Iterator_10_12.png)

skiplist迭代器,用于迭代跳表中的节点。

&nbsp;   
<a id="interface_specification"></a>
## 接口说明

1.SkipList

void Insert(const Key& key) 插入节点。

bool Contains(const Key& key) const   判断链表是否含有某个key值的接口。

Node* NewNode(const Key& key, int height) 新建节点。

RandomHeight() 随机化一个节点高度。

bool Equal(const Key& a, const Key& b) const 判断两个键是否相等。

bool KeyIsAfterNode(const Key& key, Node* n) const 如果key大于存储在“n”中的数据,则返回true。

Node* FindGreaterOrEqual(const Key& key, Node** prev) const 返回key处或key之后的最早节点。

Node* FindLessThan(const Key& key) const 返回小于key的最近节点。

Node* FindLast() const 返回最后一个节点。

2.SkipList:Node

Node* Next(int n)  获取当前节点的下一个节点

void SetNext(int n, Node* x) 设置当前节点的下个节点。

Node* NoBarrier_Next(int n)  无需内存屏障的查找下一个节点

void NoBarrier_SetNext(int n, Node* x) 无需内存屏障的设置下一个节点

3.SkipList:Iterator

bool Valid()const  如果迭代器位于有效节点,则返回true。

const Key&key()const  返回当前位置的键。

void Next() 移动到下一个位置。

void Prev() 移动到前一个位置

void Seek(const Key&target) 使用键> = target前进到第一个条目

void SeekToFirst() 位于列表中的第一个条目。

void SeekToLast() 位于列表中的最后一个条目。

&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明
需要以下文件:

"port/port.h"

"util/arena.h"

"util/random.h"

&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节
1.插入操作

插入操作借助于查找操作实现。在查找的过程中,不断记录每一层的前任节点,为新插入的节点随机产生层高;在合适的位置插入新节点,并依据查找时记录的前任节点信息,在每一层中,以链表插入的方式,将该节点插入到每一层的链接中。

链表插入指:将当前节点的Next值置为前任节点的Next值,将前任节点的Next值替换为当前节点。

2.删除操作

跳表的删除操作较为简单,依赖查找过程找到该节点在整个跳表中的位置后,以链表删除的方式,在每一层中,删除该节点的信息。

链表删除指:将前任节点的Next值替换为当前节点的Next值,并将当前节点所占的资源释放。

3.迭代

向后遍历

若迭代器刚被创建,则根据用户指定的查找范围(Start, Limit)找到一个符合条件的跳表节点;

若迭代器处于中部,则取出上一次访问的跳表节点的后继节点,作为本次访问的跳表节点(后继节点为最底层的后继节点);

利用跳表节点信息(keyvalue数据偏移量,key,value值长度等),获取keyvalue数据;

向前遍历

若迭代器刚被创建,则根据用户指定的查找范围(Start, Limit)在跳表中找到最后一个符合条件的跳表节点;

若迭代器处于中部,则利用上一次访问的节点的key值,查找比该key值更小的跳表节点;

利用跳表节点信息(keyvalue数据偏移量,key,value值长度等),获取keyvalue数据;

&nbsp;   
<a id="reference"></a>
## 参考资料

 - [leveldb源码分析之Skiplist](http://luodw.cc/2015/10/16/leveldb-05/)
 - [leveldb-handlebook](https://leveldb-handbook.readthedocs.io/zh/latest/memorydb.html#id2)
 - [leveldb](https://www.cnblogs.com/xueqiuqiu/tag/leveldb/)
 - [深夜学算法之SkipList:让链表飞](https://www.jianshu.com/p/fcd18946994e)
 - [leveldb(五):SkipList跳表](https://blog.csdn.net/weixin_36145588/article/details/76393448)


================================================
FILE: architecture/平台相关-锁-信号-原子-压缩/README.md
================================================


================================================
FILE: architecture/平台相关-锁-信号-原子-压缩/atomic/README.md
================================================


================================================
FILE: architecture/平台相关-锁-信号-原子-压缩/posix/README.md
================================================


================================================
FILE: architecture/平台相关-锁-信号-原子-压缩/snappy/README.md
================================================


================================================
FILE: doc/SA.md
================================================
# LevelDB

![](assets/leveldb_icon_09_26.png)

## Abstract
LevelDB is an open source on-disk key-value store written by Google fellows Jeffrey Dean and Sanjay Ghemawat. Inspired by Bigtable, LevelDB is hosted on GitHub under the New BSD License and has been ported to a variety of Unix-based systems, Mac OS X, Windows, and Android.

-----

## Table of Contents

- [1. Introduction](#Introduction)
- [2. Stakeholders](#stakeholders)
- [3. Context View](#Context_View)
  - [3.1 leveldb in Bigtable](#leveldb_in_Bigtable)
  - [3.2 Platform & Dependency](#Platform_Dependency)
  - [3.3 Development & Community](#Development_Community)
- [4. Functional View](#Functional_View)
- [5. Architecture](#Architecture)
  - [5.1 Module View](#Module_View)
  - [5.2 Common Design Models](#Common_Design_Models)
- [6. Evolution Perspective](#Evolution_Perspective)
- [7. Technical Debt Analysis](#Technical_Debt_Analysis)
- [8. Conclusion](#Conclusion)
- [9. References](#References)



&nbsp;   
<a id="introduction"></a>
## 1. Introduction

![](assets/level_history_11_20.png)

The concept of log-structured system is raised in 1988, and it's first implemented in 1992, for developing a Unix-like distributed operating system "Sprite". In 2006, Google published a thesis on OSDI "Bigtable: A Distributed Storage System for Structured Data". In 2011, Google made a decision to open source the storage engine under Bigtable, so LevelDB were born.

LevelDB is based on concepts from Google's Bigtable database system. The table implementation for the Bigtable system was developed starting in about 2004, and is based on a different Google internal code base than the LevelDB code. That code base relies on a number of Google code libraries that are not themselves open sourced, so directly open sourcing that code would have been difficult. Jeff Dean and Sanjay Ghemawat wanted to create a system resembling the Bigtable tablet stack that had minimal dependencies and would be suitable for open sourcing, and also would be suitable for use in Chrome for the IndexedDB implementation. They wrote LevelDB starting in early 2011, with the same general design as the Bigtable tablet stack, but not sharing any of the code.

### Features

- Keys and values are arbitrary byte arrays.
- Data is stored sorted by key.
- Callers can provide a custom comparison function to override the sort order.
- The basic operations are `Put(key,value)`, `Get(key)`, `Delete(key)`.
- Multiple changes can be made in one atomic batch.
- Users can create a transient snapshot to get a consistent view of data.
- Forward and backward iteration is supported over the data.
- Data is automatically compressed using the [Snappy compression library](http://google.github.io/snappy/).
- External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.


&nbsp;   
<a id="stakeholders"></a>
## 2. Stakeholders

To start off our analysis, we will look at the stakeholders involved with levelDB. A stakeholder is an entity of a system architecture that consists of an individual or an organization that has importance and interest to realize a system.

LevelDB is used as the backend database for Google Chrome's IndexedDB and is one of the supported backends for Riak. Additionally, Bitcoin Core stores the blockchain metadata using a LevelDB database. Minecraft: Pocket Edition uses a modified version for chunk and entity data storage. Autodesk AutoCAD 2016 also uses LevelDB.   

### [levelDB is used in Bitcoin](https://github.com/bitcoin/bitcoin/tree/master/src/leveldb)

Bitcoin uses LevelDB to store and retrieve block information.

On the subject of why LevelDB is used, core developer Greg Maxwell stated the following to the bitcoin-dev mailing list in October 2015:
>I think people are falling into a trap of thinking "It's a <database>, I know a <black box> for that!"; but the application and needs are very specialized here. . . It just so happens that on the back of the very bitcoin specific cryptographic consensus algorithim there was a slot where a pre-existing high performance key-value store fit; and so we're using one and saving ourselves some effort...

[How is Bitcoin using LevelDB? - Quroa](https://www.quora.com/How-is-Bitcoin-using-LevelDB)   
[Bitcoin Core 0.11 (ch 2): Data Storage](https://en.bitcoin.it/wiki/Bitcoin_Core_0.11_(ch_2):_Data_Storage)   

### [levelDB is used in frontend](http://leveldb.org/)

[Node.js support package on Github](https://github.com/Level/level)

The Node.js Community built [level](https://github.com/Level/level) (Fast & simple storage - a Node.js-style LevelDB wrapper).

[Richard Astbury](https://coderead.wordpress.com/2013/04/04/node-js-leveldb/):
>This article is based on a recent NodeUp podcast dedicated to LevelDB, I recommend listening to it.   
I have played around a bit with Level, and I’ve been really impressed by it’s speed, ease and simplicity. You should give it a go.

![](assets/SA_StakeHolder_10_09.png)

![](assets/SA_Stakeholder_sponsors_level_10_09.png)

### [levelDB is used in Facebook.RocksDB](https://rocksdb.org/)

![](assets/leveldb_rocksdb_11_20.png)

RocksDB is a high performance embedded database for key-value data. It is a fork of LevelDB by Facebook optimized to exploit many central processing unit (CPU) cores, and make efficient use of fast storage, such as solid-state drives (SSD), for input/output (I/O) bound workloads.

#### Potential Use-cases of RocksDB:   
RocksDB can be used by applications that need low latency database accesses. A user-facing application that stores the viewing history and state of users of a website can potentially store this content on RocksDB. A spam detection application that needs fast access to big data sets can use RocksDB. A graph-search query that needs to scan a data set in realtime can use RocksDB. RocksDB can be used to cache data from Hadoop, thereby allowing applications to query Hadoop data in realtime. A message-queue that supports a high number of inserts and deletes can use RocksDB.


&nbsp;   
<a id="context_view"></a>
## 3. Context View

leveldb is a BigTable-inspired key-value database library. It’s available for Unix based systems, Mac OS X, Windows, and Android… LevelDB is not a database server like other other key-value stores like Redis or Membase. Instead, it would most likely be used as an embedded database for other applications, much the way SQLite or Berkley DB are used. The technical advantage to using LevelDB instead of other key-value stores is its support for ordered data. Also, its BSD license is more liberal than the GPL Sleepycat license of Berkley DB.”


&nbsp;   
<a id="leveldb_in_bigtable"></a>
### 3.1 leveldb in Bigtable

Bigtable is built on several other pieces of Google infrastructure such as levelDB and the distributed Google File System (GFS).   
The levelDB SSTable file format is used internally to
store Bigtable data. An SSTable provides a persistent,
ordered immutable map from keys to values, where both
keys and values are arbitrary byte strings. Operations are provided to look up the value associated with a specified key, and to iterate over all key/value pairs in a specified key range. Internally, each SSTable contains a sequence of blocks (typically each block is 64KB in size, but this is configurable). A block index (stored at the end of the SSTable) is used to locate blocks; the index is loaded into memory when the SSTable is opened. A lookup can be performed with a single disk seek: we first find the appropriate block by performing a binary search in the in-memory index, and then reading the appropriate block from disk. Optionally, an SSTable can be completely mapped into memory, which allows us to perform lookups and scans without touching disk.

![](assets/SA_Bigtable_10_09.jpg)


<a id="platform_dependency"></a>
### 3.2 Platform & Dependency

#### [Levelup](https://github.com/Level/levelup)
Levelup is a Node.js project that aims to provide a common, portable interface to a multitude of LevelDB forks such as Hyperdex. Check out the many flavours of LevelDB made available though this project.

#### Embeddable & Networkable
LevelDB is embedded, but can be networked adding protocols such as http, tcp or udp to your process.


<a id="development_community"></a>
### 3.3 Development & Community

A platform that plays an important role during development is GitHub, which is used for code versioning, issue tracking and project management.[leveldb on Github](https://github.com/google/leveldb)

Discussion group for LevelDB. LevelDB is a open source library that implements a fast persistent key-value store.[leveldb Group on Google Forum](https://groups.google.com/forum/#!forum/leveldb)


&nbsp;   
<a id="functional_view"></a>
## 4. Functional View

LevelDb is the two great spirit level engineers is an open-source project, in short, LevelDb is capable of processing one billion scale Key-Value persistent data storage C++ Library. As described above, the two is the design and implementation of Bigtable, if the understanding of Bigtable, should know that there are two core in distributed storage system, the influence of part: Master Server and Tablet Server. The Master Server store data management and distributed scheduling, the distributed data storage and read and write operation is completed by the Tablet Server, and LevelDb can be understood as a simplified version of the Tablet Server.

LevelDb has the following advantages:

- First of all, LevelDb is a k-v system for persistent storage, k-v system and Redis this type of memory is different, LevelDb doesn't like Redis eat memory, but most of the data stored to disk.
- Secondly, LevleDb when storing data, is based on the records of key value orderly storage, is adjacent to the key value in the storage file is stored in sequence, and the application can customize the key size comparison function, LevleDb will be in accordance with the comparison of user defined function sequentially stored the records.
- Again, like most k-v systems, the operation interface of LevelDb is very simple, basic operation including written records, delete records recording and reading. Support for multiple operating atomic batch operation.
- In addition, LevelDb supports data snapshot (snapshot) function, the read operation is not affected by the write operation, can always see consistent data in the reading process.
- In addition, LevelDb also supports data compression to reduce the operation, the storage space and increase IO efficiency have direct help.

### Fast Storage

LevelDb performance is very outstanding, the official website of the random write performance up to 400000 records per second, while the random read performance up to 60000 records per second. In general, the write operation of LevelDb is much faster than the read operation, and the sequential read operation is much faster than random read and write operations. Why is this, see our follow-up to the introduction of LevelDb Gu Yanwu, estimated that you will understand the intrinsic reason.

### Snapshot

In addition, LevelDb supports data snapshot, the read operation is not affected by the write operation, can always see consistent data in the reading process.

On every write, we generate a sequence number. We store the number locally and use the oldest still living snapshot as the oldest version that we have to retain when doing compaction work.

### Recover

![](../architecture/DB/assets/version_recover_10_12.jpeg)

LevelDB will rebuild the lateset version by the leverage of `.manifest ` whenever levelDB starts, which is called recovery.


&nbsp;   
<a id="architecture"></a>
## 5. Architecture

![](assets/leveldb_UML_10_01.png)

![](../architecture/SSTable/assets/Minor_Compaction_10_05.png)


### Log-Repair pattern

![](../architecture/DB/assets/two_log_11_09.jpeg)

Before the data is written into memtable, it is firstly stored in the log files. The log files are frozen and collected as long as the memtable is dumped into SSTable. If server suddenly crashes, we can restart it and recover the data from the log.

### MVCC - Multiple Version Concurrency Control

A querying request is accepted and executed in the current version, and an updating request is executed on a new version, which is later added in the version set.


&nbsp;   
<a id="module_view"></a>
## 5.1 Module View

![](assets/leveldb_flow_11_21.png)

As can be seen from the diagram, a LevelDb static structure consists of six main parts: several main file memory in the MemTable and Immutable MemTable and disk: Current file, Manifest file, log file and SSTable file. Of course, LevelDb in addition to the six main parts and some auxiliary files, but these six files and data structure is the main body of the LevelDb elements.

The Log file is consistent with LevelDb and Memtable and Bigtable in the paper., When applied to write a Key:Value record, LevelDb will first go to the log file write, After the success of the record is inserted into the Memtable, So basically completed write operations, For a write operation only involves writing a sequential write once memory and disk, So this is why the main reason LevelDb write fast.

Effect of Log files in the system is mainly used for system crash recovery without loss of data, if there is no Log file, because the written records began is stored in main memory, at this time if the system crashes, the memory of the data have not yet Dump to disk, so the loss of data (Redis problem). To avoid this situation, LevelDb in writing to the memory before the first operation records to the Log file, and then re entered in the memory, so that even if the system crashes, can also restore memory in the Memtable from the Log file, will not cause the loss of data.

When the Memtable is inserted into the data memory to a limit, Need to be exported to the external memory record file, The LevleDb generates a new Log file and Memtable, The original Memtable becomes Immutable Memtable, Seeing the name of a thing one thinks of its function, This means that the contents of Memtable and cannot be changed, Can read but not write or delete. The new coming data is recorded in the new Log file and Memtable, LevelDb background scheduling can export data from a Immutable Memtable to the disk, the formation of a new SSTable file. The formation of SSTable is composed of data in memory has derived and Compaction after operation, and all of the file SSTable is a hierarchical structure, the first layer is the second layer of Level 0, Level 1, and so on, the level gradually increased, which is why called LevelDb causes.

The SSTable file is Key order, That is in the file and key records before Key records, So are all of the Level SSTable, But one thing to note here is: Level 0 SSTable file (..sst) and other Level files compared with special: the levels of.Sst file, Two files may have overlapping key, For example there are two level and 0 SST file, The file A and file B, The file A is the key range: {bar, car}, The file B is the Key range{blue,samecity}, It is very likely that the existence of two documents are key= "blood" record. For other Level SSTable file, does not have the same level of.Sst files within key overlap phenomenon, is that any Level L two.Sst files, you can guarantee that their key values are not overlap. This requires special attention, you will see the difference behind many operations are caused due to this reason.


&nbsp;   
<a id="common_design_models"></a>
## 5.2 Common Design Models

In this section, common designs that are used and standardized in the development of levelDB are described.

### Manifest storage

A file in SSTable belongs to a certain level, and its storage record is key ordered, then there must be a minimum in the key file and key, this is very important information, LevelDb should take note of these information. Manifest is doing, it records the management information of each SSTable file, for example, which belongs to the Level, the name of the file name, what is the minimum key and maximum key. Below is a schematic Manifest storage content:

![](assets/SA_Manifest_10_09.jpg)

### Log File

The main action section about log in the LevelDb file system recovery, to ensure no loss of data. Because before the record to write into the memory of the Memtable, Will be the first to write to the Log file, So even if the system fault, The data in the Memtable before the Dump SSTable file to disk, LevelDB can also be based on the Memtable data structure log file recovery memory, The system will not result in loss of data, LevelDb at this point and Bigtable are identical.

Below we have a look the specific physical and logical layout of the log file is what, LevelDb for a log file, Will cut it down to a physical unit of Block based on 32K, Each reading unit to read a Block as basic unit, The log file which is composed of 3 Block, So from the physical layout of speaking, A log file is composed of continuous 32K size Block.

![](assets/SA_Logfile_10_09.jpg)

### Cache

![](../architecture/util/LRU/assets/handle_table_09_24.png)

![](../architecture/util/LRU/assets/SharededLRUCache_结构_09_24.png)

This is the structure of the LRUCache, which is used in `TableCache` and `BlockCache`.

A read operation if the record is not found in the memory of memtable, to multiple disk access operations. The optimal condition of hypothesis, Is the first in the new level file 0 found in this key, It is also necessary to read the 2 disk, Once the SSTable file in the index part is read into memory, The index can determine the key is stored in a block according to second times is read; the block content, And then look for the key in memory corresponding value.

Two different Cache: Table Cache and Block Cache into levelDb. The Block Cache is configured in the optional, in the configuration file specifies whether to open this function.

#### TableCache

![](assets/SA_TableCache_10_10.jpg)

The Figure is the structure of table cache. In Cache, The value of key is the SSTable file name, Value consists of two parts, One is the file pointer to the disk to open SSTable file, This is for the convenience of reading; another is Table structure pointer to the corresponding memory in the SSTable file, The table structure in memory, Save the index contents of SSTable and cache_id used to indicate block cache, of course, there are also some other content.

For example, in the get (key) read operation, if levelDb key was determined in a level under a file A key range range, so need to judge Is it right? Really contains the k-v file A. At this time, levelDb will first look for Table Cache, see the file is in the cache, if found, then according to the index part can find which block contains the key. If the file is not found in the cache, then open the SSTable file, the index part is read into memory, and then insert the Cache inside, go to index positioning which block contains the Key . If the file which contains the key block, you need to read the block content, this is the second time to read.

#### BlockCache

![](assets/SA_BlockCache_10_10.jpg)

Block Cache is to accelerate this process, the figure is a schematic diagram of the structure. Where key is the file cache_id and the block in the starting position in the file block_offset. Value is the Block content.

If the levelDb finds the block in block cache, you can avoid reading data, block content in cache find key value on the line, if not found? Then read into the block and insert it in block cache. This is the levelDb by two cache to accelerate reading speed. From here we can see that, if the data locality read better, that is to say to read most of the data in cache can be read, then read the efficiency is very high, and if it is on the key sequential reads efficiency should also be good, because once after reading can be repeatedly reused. But if the random read, you can deduce that its efficiency.

### Memtable

![](../architecture/DB/assets/memtable_10_07.jpg)

This section describes the data structure of Memtable memory, an important position in the whole system of Memtable is self-evident. Overall, All k-v data is stored in the Memtable, Immutable Memtable and in SSTable, Immutable Memtable and from the structure of Memtable is exactly the same, The only difference is that it is read-only, Do not allow write operations, While the Memtable is allowed to write and read. When Memtable writes data to a specified number of memory, it is automatically converted to Immutable Memtable, waiting for Dump to the disk, the system will automatically generate a new Memtable for write operation to write new data, understand the Memtable, then Immutable Memtable natural be nothing difficult.

LevelDb MemTable provides k-v data will be written, The operation interface is deleted and read the k-v records, But the fact is that Memtable does not exist in the real delete, delete a Key Value in Memtable is implemented as insert a record, But will hit the delete a Key marker, Really delete operation is Lazy, Will in the future in the process of Compaction to remove the k-v.

Of note, LevelDb Memtable k-v on Key is based on the size of the order in the system memory, insert a new k-v, LevelDb to put the k-v into the right position in order to keep this Key order. In fact, LevelDb Memtable is just an interface class, real operation is done through behind SkipList, including the insertion operation and read operations, so the core data structure of Memtable is a SkipList.

### SSTable

SSTable is a Bigtable critical, the same is true for the LevelDb, the LevelDb SSTable implementation details can also help to understand some of the implementation details in Bigtable.

Static layout structure in this section is mainly about SSTable, the hierarchical structure is formed and we put behind the Compaction section details. This section introduces the SSTable a file in the physical layout and logical structure, the understanding of the operation process of LevelDb very helpful.

![](../architecture/SSTable/assets/leveldb_sstable_structure_10_03.png)

#### Logical Layout

![](assets/SA_SSTable_format_10_09.jpg)

As you can see from this figure, from the large side, .sst files can be divided into data storage and data management, k-v data storage area storing the actual data management areas, provide some index management data, is more rapid and convenient to find the corresponding record. Two regions are in the block on the basis of that file, in front of the plurality of actual k-v data storage, data storage management behind the data management area. Data management is divided into four different types: Purple Meta Block, data index block red MetaBlock index and a blue and a tail of file block.

#### Data Index

![](assets/SA_SSTable_dataindex_10_09.jpg)

This figure is a schematic diagram of the internal structure of data index. Again, Data Block k-v records in the Key arrange the, Each record data index is established on the index information of a Data Block, Each index information includes three contents, Block shown in the figure, the index of index i: the first field red part of the record is greater than or equal to the maximum key data key block i value, The second field points out that the data block I in .sst the starting position in the file, The third field points to Data Block i size (sometimes a data compression).

#### Block data structure

![](../architecture/SSTable/assets/Block_structure_10_02.png)

As can be seen from the graph, its interior is divided into two parts, the front is a k-v record, the order is based on the Key value from small to large, in the Block tail is some "restart" (Restart Point), is actually some pointer, points out that the Block contents in some recording position.

"The restart point "is stem what of? We have repeatedly stressed, The Block content in the k-v record is ordered according to the size of Key, In this way, Two records of adjacent may Key some overlap, For example, key I="the Car", Key i+1="the color",Then there is overlap"the c", In order to reduce the storage capacity of Key, Key i+1 can only store and a different Key part"olor", The common part can be obtained from Key I. Recording of Key content in Block part is so storage, the main purpose is to reduce the storage overhead. "The restart point "the meaning is: at the beginning of this record, no longer take only record the various Key components, but re recording all values of Key, assuming Key i+1 is a restart, then Key will complete storage" the color ", rather than a simple" olor "mode. Block tail is pointed out which records are the restart point.

#### Record format

![](../architecture/SSTable/assets/Block_data_format_10_02.png)

In the Block content area, The internal structure of each k-v record is what? The figure gives the detailed structure, Each record contains 5 fields: key sharing length, For example, the "olor" record, The key and a record shared Key part length is "the C" length, 5; key non shared length, For "olor", 4; value Key:Value Value points out that the length of the length, In the Value content stored in the field behind the actual Value value; while the key non shared content is the actual storage "olor" the Key string.

### Compaction

For LevelDb, the recording operation is very simple, just write a delete delete records label even if finished, but read the record is more complex, need to take in order to find fresh in memory and all levels of file, a very high price. In order to speed up the reading speed, levelDb took compaction to the existing record of finishing compression, by this way, to remove some no longer valid k-v data, reduce the data size, reduce the number of files etc.

The levelDb compaction mechanism and process and Bigtable are basically the same, Bigtable says that three types of compaction:, minor, major and full. The so-called minor Compaction, is the memtable data to an SSTable file; major compaction is associated with different levels of SSTable files, and full compaction is to merge all SSTable.

LevelDb contains two of them, [minor](#minor) and [major](#major).

<a id="minor"></a>
#### Minor Compaction

![](../architecture/SSTable/assets/Minor_Compaction_10_05.png)

As can be seen from the figure, when the number of memtable to a certain extent will be converted to immutable memtable, this time not to write record, only read content from k-v. Introduced before, immutable memtable is in fact a multilevel queue SkipList, the record is based on the key ordered. So the minor compaction implementation is very simple, Is in accordance with the ascending immutable memtable traversal records, And then write a new SSTable file in level 0, The establishment of index data file is finished, So that the completion of a minor compaction. From the figure can be seen, In a deleted record, In the minor compaction process does not actually delete the record, The reason is very simple, Here only know to delete key records, But where is this k-v data? The need of complex search, So when minor compaction does not delete, But will the key as a record to the file, As for the real delete operation, After the higher level of compaction will do.

When the number of a level SSTable file exceeds the set value, levelDb will select a file from the level SSTable (level> 0), the high level of the level+1 SSTable merger documents, this is major compaction.

We know that in more than 0 levels, each SSTable file in Key is to orderly storage, but also between different documents to key range (between the minimum key and maximum key files) will not have any overlap. The Level 0 SSTable file some special, although each file is sorted according to the Key, but since level 0 file is generated directly through minor compaction, so any two level two sstable files under key 0 may have overlapping range. So when major compaction, For more than 0 level level, Choose one of the file on the line, But for level 0, Specify a file, The level is likely to have other SSTable files of the key range and the document overlap, This case, To find all the overlap of the document and level 1 files are merged, Level 0 when the file selection, There may be multiple files in major compaction.

LevelDb compaction in the selection of a level, You also choose any specific files to compaction, LevelDb is a small skills here., That is to say to take turns, For example, the file A compaction, So the next time is in key range is next to the file A file B compaction, So each file will have the opportunity to take turns and high-level level file merge.

<a id="major"></a>
#### Major Compaction

![](../architecture/SSTable/assets/Major_Compaction_10_05.png)

The process of Major compaction are as follows: the multiple files using multi merge sort of way, in order to find out where the minimum Key record, is for all the recording multiple files in the re ranking. After taking a certain standard to judge whether the Key also needs to be saved, if not saved value, then directly away, if you still need to save, then write it to the new generation of level L+1 layer in a SSTable file. So on the one one k-v data processing, formed a series of new L+1 data file, before the L layer file and the L+1 layer in compaction file data at this time has no meaning, so delete all. This completes the combined process of L layer and L+1 layer file record.

Then the major compaction process, determine what is a k-v record is abandoned? Which is a standard: for a key, if less than this Key exists in L layer, the k-v in the major compaction process can drop. Because we the above analyses, the level below the L file if there is the same Key record, so that for Key, there is more fresh Value, so Value in the past is no meaning, so they can be deleted.

### Version

Version saves the file all the information of the current disk and memory, generally only a Version called "version current" (the current version). 

(old)Version + VersionEdit --> (new)Version

VersionEdit will be saved to the MANIFEST file, as data recovery will be from the MANIFEST file to read out data reconstruction.

![](../architecture/DB/assets/Version_VersionEdit_11_08.png)

When a Compaction ends (will generate a new file, before the merger documents need to be deleted), Leveldb creates a new version as the current version, the original version will change into the current version of history.


![](../architecture/DB/assets/VersionSet_11_08.png)

VersionSet is the set of all Version, manages all the survival of Version.
VersionEdit said the changes between Version, equivalent to delta increment, expressed increased the number of files, delete the file. The graph below shows the relationship between them.


&nbsp;   
<a id="evolution_perspective"></a>
## 6. Evolution Perspective

LevelDB is based on concepts from Google's Bigtable database system. The table implementation for the Bigtable system was developed starting in about 2004, and is based on a different Google internal code base than the LevelDB code. That code base relies on a number of Google code libraries that are not themselves open sourced, so directly open sourcing that code would have been difficult. Jeff Dean and Sanjay Ghemawat wanted to create a system resembling the Bigtable tablet stack that had minimal dependencies and would be suitable for open sourcing, and also would be suitable for use in Chrome for the IndexedDB implementation. They wrote LevelDB starting in early 2011, with the same general design as the Bigtable tablet stack, and opened leveldb to the community. levelDB is now still the low-level storage engine behind Google's Bigtable and MapReduce servers.


&nbsp;   
<a id="technical_debt_analysis"></a>
## 7. Technical Debt Analysis

In 2011, the Google/Bigtable org decided to reorganize the storage engine behind the Bigtable, leading to the birth of LevelDB. Thus they got rid of the old dependency and technical debt. And thanks for the open source community, LevelDB has developed at great success.

### Defect Debt

LevelDB is used in bitcoin and it had a bug that, under rare conditions, could cause it to consistently return not found on records that were really there. . . Leveldb fixed this serious bug in a minor update. But deploying a fix like this in an uncontrolled manner in the bitcoin network would potentially cause a fork in the consensus state; so any such fix would need to be rolled out in an orderly manner.

### Codestyle Debt

- Mush use of type erasure impedes the progress of reading source code and maintain the project.
- I've once noticed that the iteration_expression in for-loop is located at the end of the code block, which is more than 100 lines below the for-loop.

### Code Quality

[![CodeFactor](https://www.codefactor.io/repository/github/rsy56640/leveldb/badge)](https://www.codefactor.io/repository/github/rsy56640/leveldb)

Some files quality detection:

![](assets/SA_code_quality_11_18.png)

In fact, it's absolutely more than B+ rating, though many functions and modules in levelDB is sort of large and complex. It is of great necessity for a storage engine to do some complex work.


&nbsp;   
<a id="conclusion"></a>
## 8. Conclusion

LevelDB is designed to be used as persistent storage engine in Google Bigtable. It provides users with fast k-v storage and snapshot, but without server and client backend. It has been widely used in database domain, such as bitcoin storage engine. Facebook has developed [RocksDB](https://github.com/facebook/rocksdb) based on levelDB. In addition, levelDB made a good start for distributed storage.


&nbsp;   
<a id="references"></a>
## 9. References

- [The principle of LevelDb analysis](https://www.programering.com/a/MjMyMTNwATQ.html)
- [What are the keys used in the blockchain levelDB (ie what are the key:value pairs)?](https://bitcoin.stackexchange.com/questions/28168/what-are-the-keys-used-in-the-blockchain-leveldb-ie-what-are-the-keyvalue-pair)
- [Node.js + LevelDB](https://coderead.wordpress.com/2013/04/04/node-js-leveldb/)
- [The Google forum for levelDB](https://groups.google.com/forum/#!forum/leveldb)   
- [Issues of levelDB on Github](https://github.com/google/leveldb/issues)

================================================
FILE: doc/Understand配置及使用/README.md
================================================
clone [这个仓库](https://github.com/rsy56640/leveldb) 到本地

打开 understand,点击 File -> New -> Project   

![](assets/new_project.png)  

选择 C++,Fuzzy

![](assets/fuzzy.png)

![](assets/1.png)

![](assets/add.png)

点击 Add a Directory

在 Directory 里选择 clone下来的仓库的位置。

![](assets/add_leveldb.png)

完成

![](assets/complete.png)

额外地,可以选择设置 **只读**,以防不小心修改代码。

点击 Project -> Configure Project

![](assets/readonly.png)


## 关于使用

网上关于 understand 这一强大的源码阅读工具的讲解很多,我在这里列举一些:

打开 include/leveldb/db.h

![](assets/db_h.png)

在 文件 上点击右键,

![](assets/class_UML.png)

看到如下图所示的 UML类图(当然,我这个是缩小之后的)

![](assets/class_UML2.png)

对于比较复杂的函数,点击右键,查看控制流程:

![](assets/control_flow.png)

![](assets/control_flow2.png)

其他的,比如 **搜索**、**跳转**、**查询依赖** 等功能也很强大,就不一一列举了。

================================================
FILE: doc/plan.md
================================================
# Plan

**推荐使用的源码阅读工具:Source Insight / Understand (Sci-Tool)**  

项目地址:   
[rsy56640/leveldb - forked](https://github.com/rsy56640/leveldb) - 这个是我之前fork的,直接clone下来就ok    
[google/leveldb](https://github.com/google/leveldb) - 这个是最新的

提交文件放在相应目录下,文件名称:`xxx - 时间 - 作者`。    
**所有 `README` 文档均由组长完成。**   

![](assets/JIM项目结构.png)

![](assets/taobao1.png)


-----

# 所有模块均可参考 [reference](https://github.com/rsy56640/read_and_analyse_levelDB/tree/master/reference) 前5的 链接!!!!!
# 要求是:模块功能 和 对外接口 必须要弄清楚,但实现细节不要求看懂(因为各位以后都很可能不写cpp)
# 在写 模块文档 前一定要参考 [项目文档格式说明](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E6%96%87%E6%A1%A3%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E.md),文档格式直接copy [文档格式模板.md](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E6%96%87%E6%A1%A3%E6%A0%BC%E5%BC%8F%E6%A8%A1%E6%9D%BF.md)

-----

## 所有 "体系结构的描述" 汇总在该模块的 `README.md` 文件下(我负责写),总体的描述我最后再写。

-----

&nbsp;   
**软件体系架构恢复**:(即本学期课程目标)

- [总体架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E8%BD%AF%E4%BB%B6%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.md)
  - [数据库逻辑架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/README.md) ![](https://img.shields.io/badge/leveldb--db-25%25-orange.svg)
  - [底层存储架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/README.md) ![](https://img.shields.io/badge/leveldb--table-70%25-yellow.svg)
  - [基础工具库](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/README.md) ![](https://img.shields.io/badge/leveldb--util-70%25-blue.svg)
  - [其他:平台相关-锁-信号-原子-压缩](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/%E5%B9%B3%E5%8F%B0%E7%9B%B8%E5%85%B3-%E9%94%81-%E4%BF%A1%E5%8F%B7-%E5%8E%9F%E5%AD%90-%E5%8E%8B%E7%BC%A9/README.md) 

-----

&nbsp;   
## 计划开始时间:2018-09-19   

&nbsp;   
### 09-19 ~ 09-21

全组成员安装 understand 源码阅读软件,阅读 [文档格式说明](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E6%96%87%E6%A1%A3%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E.md)    
阅读 [参考资料](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/README.md),了解 `leveldb` 的大概结构。   


&nbsp;   
### 09-22 ~ 09-24

因为是第一次,又是中秋节,就少布置一些任务。   

1. 阅读 [这篇文档](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/Understand%E9%85%8D%E7%BD%AE%E5%8F%8A%E4%BD%BF%E7%94%A8/README.md),配置及使用 understand.
2. 阅读下面4篇文章,了解 `leveldb` 的整体构架。

<a></a>

- [LevelDB 源码分析:主体结构](http://cighao.com/2016/08/14/leveldb-source-analysis-02-structure/)  
- [LevelDB实现总结](http://morefreeze.github.io/2016/05/LevelDB-Summarize.html)
- [我的leveldb总结](https://blog.csdn.net/poi7777/article/details/49124491)
- [LevelDB详解](https://blog.csdn.net/linuxheik/article/details/52768223)



&nbsp;   
### 09-26 ~ 10-02 第五周

从一些基础工具库开始读起。

- [Status]():杜致
  - `util/status.cc`, `include/leveldb/status.h`, 文档放置于 "DB" 文件夹下
  - 可参考:
      - [leveldb源码分析之Status](http://luodw.cc/2015/10/15/leveldb-03/)
  - 总结:返回状态,封装了错误号和错误信息,统一进行处理
- [Options, Env]():季宇恒
  - `util/options.cc`, `include/leveldb/options.h`, `util/env.cc`, `include/leveldb/env.h`, 文档名叫 "Options.md" 和 "Env.md"
  - 指导:抽象了整个 db 的环境与配置选项,主要搞清楚接口与参数的说明
  - 可参考:
      - [leveldb](https://dirtysalt.github.io/html/leveldb.html)
      - [文件系统及Env-leveldb源码剖析(5)](http://www.pandademo.com/2016/03/file-system-and-env-leveldb-source-dissect-5/)
  - 总结:
- [Comparator]():毛凯
  - `util/comparator.cc`, `include/leveldb/comparator.h`, `db/dbformat.h`, `db/dbformat.cc`
  - 指导:`Comparator` 下主要有两种实现:`BytewiseComparatorImpl`, `InternalKeyComparator`,区分两者的 **功能**,以及 **分别在什么情况下使用**
  - 总结:
- [ParsedInternalKey, InternalKey, LookupKey](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/dbformat_key-2018-10-01-ss.md):苏胜
  - `db/dbformat.h`, `db/dbformat.cc`, 文档名叫 "dbformat_key - 2018-10-01 - ssh.md", 文档放置于 "DB" 文件夹下
  - 指导:db 内部包装的 key 结构,需要阐释清楚 **具体结构**,**含义**,**用途** 以及 **依赖于key的操作**
  - 可参考:[leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
  - 总结:对 key 的包装,内含 `user_key`, `SequenceNumber`, `ValueType`
- [Slice](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Slice%20-%202018-09-16%20-%20rsy.md):任思远
  - `include/leveldb/slice.h`
  - 总结:为操作数据的方便,将数据和长度包装成 Slice 使用,直接操控指针避免不必要的数据拷贝。就是个简易的 **`string`**
- [Arena](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/Arena/arena%20-%202018-09-30%20-%20rsy.md):任思远
  - `util/arena.h`, `util/arena.cc`
  - 总结:Arena 主要与 MemTable 关联使用,实际主要用于 SkipList 中的 Node 内存分配,统一 MemTable 的内存分配需求,减少内存分配的实际系统调用次数(尤其针对小块内存)
- [Coding](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/Coding/coding%20-%202018-09-06%20-%20rsy.md):任思远
  - `util/coding.h`
  - 总结:用于压缩 int 所占的存储空间的一套编解码工具
- [Cache](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/LRU/cache%20-%202018-09-20%20-%20rsy.md):任思远
  - `include/leveldb/cache.h`, `util/cache.cc`
  - 总结:在 TableCache 用于缓存 SSTable 的对应索引元数据
- [TableCache](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/TableCache%20-%202018-09-30%20-%20rsy.md):任思远
  - `db/table_cache.h`, `db/table_cache.cc`
  - 总结:`TableCache`缓存的是 sstable 的索引数据,k-v格式为 `file_number` - `TableAndFile`
- [Iterator](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Iterator%20-%202018-10-01%20-%20rsy.md):任思远
  - `include/leveldb/iterator.h`, `table/iterator.cc`
  - 总结:leveldb 中对 key 的查找和遍历,上层统一使用 `Iterator` 的方式处理,屏蔽底层的处理,统一逻辑。 提供 `RegisterCleanup()` 可以在 `Iterator` 销毁时,做一些清理工作(比如释放 `Iterator` 持有句柄的引用)
- [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md):任思远
  - `include/leveldb/write_batch.h`, `db/write_batch.cc`
  - 总结:leveldb 内部的一个批量写的结构,在 leveldb 为了提高插入和删除的效率,在其插入过程中都采用了批量集合相邻的多个具有相同同步设置的写请求以批量的方式进行写入。`WriteBatch` 是一个连续字节流的表示,是将所有的请求序列化称为连续的字节流
- ![](https://img.shields.io/badge/leveldb--table-1%25-lightgrey.svg) ![](https://img.shields.io/badge/leveldb--util-30%25-yellow.svg)



&nbsp;   
### 10-03 ~ 10-09 第六周(演讲: 10-10)

这周看一些有趣的构件。

- [BloomFilter]():毛凯
  - `util/bloom.cc`
  - 指导:布隆过滤器的**效果**,**缺点** 以及 **何时刷新**(我猜的,可能不)
  - 可参考:
      - [BloomFilter - 基本理论](https://www.jianshu.com/p/181f40047834)
      - [布隆过滤器的原理和实现](https://github.com/cpselvis/zhihu-crawler/wiki/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E5%AE%9E%E7%8E%B0)
  - 总结:
- [Logging]():季宇恒
  - `util/logging.h`, `util/logging.cc`
  - 可参考:
  - 总结:
- [skiplist]():苏胜
  - `db/skiplist.h`, 放置于 `util/memtable` 文件夹下,这个给 **2周** 时间来看
  - 指导:跳表是工程中常见的数据结构,用于快速的kv查找,优点是比各种 BST(AVL,红黑树)易于实现,而且效率也高。主要思想是:如何在链表中维护 “某种结构” 以获得类似二分查找的性质
  - 可参考:
      - [leveldb源码分析之Skiplist](http://luodw.cc/2015/10/16/leveldb-05/)
      - [leveldb-handlebook](https://leveldb-handbook.readthedocs.io/zh/latest/memorydb.html#id2)
      - [leveldb](https://www.cnblogs.com/xueqiuqiu/tag/leveldb/)
      - [深夜学算法之SkipList:让链表飞](https://www.jianshu.com/p/fcd18946994e)
      - [leveldb(五):SkipList跳表](https://blog.csdn.net/weixin_36145588/article/details/76393448)
- [FileNumber, FileName]():杜致
  - `db/filename.h`, `db/filename.cc`, `db/dbformat.h`
  - 指导:
      - db 创建文件时会按照规则将 `FileNumber` 加上特定后缀作为文件名。 所以, 运行时只需要记录 `FileNumber`(uint64_t)即可定位到具体的文件路径,省掉了字符串的麻烦。 `FileNumber` 在 db 中全局递增
      - db 中的文件用文件名区分类型
  - 可参考:
      - [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
      - [LevelDB源码解析11.文件序号](https://zhuanlan.zhihu.com/p/35343043)
  - 总结:
- [BlockHandle, Footer](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/BlockHandle%26Footer%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/format.h`, `table/format.cc`
  - 总结:
      - `BlockHandle` 封装了 `Block` 的元信息(位于 sstable 的 offset/size)
      -  `Footer` 用来存储 `meta index block` 与 `index block` 在 sstable 中的索引信息,另外尾部还会存储一个 `magic word`。落盘和读取时会调用相关接口
      - `ReadBlock()` 从随机文件里面读取 `Block` 出来,对于这个 `Block` 的位置的话由 `handle` 提供(注意每个 `Block` 后面还有 `crc` 和 `type`)
- [FilterPolicy](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/FilterPolicy%20-%202018-10-03%20-%20rsy.md):任思远
  - `include/leveldb/filter_policy.h`
  - 总结:上层用于查找 key,跳过一定没有出现 key 的 sstable
- [FilterBolck](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/FilterBlock%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/filter_block.h`, `table/filter_block.cc`
  - 总结:`filter block` 就是 `meta block`,用来存储一些过滤器相关的数据。把要输出的结果按照想要的格式整理好,在内存中放置好。之后由 `log_writer` 写入文件(`BlockBuilder` 同理)。并且提供 `Reader` 来查找可能存在的 `key`
- [Block](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Block%20-%202018-10-02%20-%20rsy.md):任思远
  - `table/block.h`, `table/block.cc`, `table/block_builder.h`, `table/block_builder.cc`
  - 总结:存储数据,通过 `BlockHandle` 获取 `Block`(offset 和 size),并封装了 `Block::Iter` 用于上层调用,由 `BlockBuilder` 创建
- [IteratorWrapper](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Iterator_Wrapper%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/iterator_wrapper.h`
  - 总结:缓存了 `valid`, `key`,并管理 iter 资源
- [TwolevelIterator](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/TwoLevelIterator%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/two_level_iterator.h`, `table/two_level_iterator.cc`
  - 总结:将对 `Table` 的遍历封装,对外展现如同线性遍历。
- [Builder](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Builder%20-%202018-10-03%20-%20rsy.md):任思远
  - `db/builder.h`, `db/builder.cc`
  - 总结:`BuildTable()`:为 level-0 创建 `Table`,并把 metadata 存到 `FileMetaData* meta`
- [Memtable](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Memtable%20-%202018-10-04%20-%20rsy.md):任思远
  - `db/memtable.h`, `db/memtable.cc`
  - 总结:存储 k-v 插入、删除的操作(并不真正删除,只打标记)并且将数据序列化,在写 level-0 时将 `iter` 传入 `BuildTable` 写入文件
- [Table](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Table%20-%202018-10-04%20-%20rsy.md):任思远
  - `include/leveldb/table.h`, `table/table.cc`, `table/table_builder.cc`, `include/leveldb/table_builder.h`
  - 总结:
      - `Table` 类主要完成 sstable 的读取逻辑
      - `TableBuilder` 类用于构建 sstable,将 `data block`, `filter block`, `meta index block`,  `index block`, `footer` 写入文件
- [Merge](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/MergingIterator%20-%202018-10-05%20-%20rsy.md):任思远
  - `table/merge.h`, `table/merge.cc`
  - 总结:多路 Iterator 归并称为一个 Iterator 进行遍历,用于 `DBImpl::NewInternalIterator()` 中收集所有 iterator(memtable, imm memtable, sstable)然后统一处理
- [Compaction](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Compaction%20-%202018-10-05%20-%20rsy.md):任思远
  - `db/version_set.h`, `db/version_set.cc`
  - 总结:compaction 是执行 LSM-tree 中 merge 的过程
      - minor compaction 用于内存到外存的迁移过程
      - major compaction 用于 level 之间的迁移
- [LSM](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/LSM%20-%202018-10-06%20-%20rsy.md):任思远
  - 总结:放弃磁盘读性能来换取写的顺序性
- ![](https://img.shields.io/badge/leveldb--db-25%25-blue.svg) ![](https://img.shields.io/badge/leveldb--table-70%25-orange.svg) ![](https://img.shields.io/badge/leveldb--util-70%25-ff69b4.svg)



&nbsp;   
### 10-10 ~ 10-16 第七周

写 [SA 文档](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md),具体见 [进度报告](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E7%AC%AC%E5%8D%81%E5%9B%9B%E7%BB%84%20-%20%E8%BF%9B%E5%BA%A6%E6%8A%A5%E5%91%8A.md)。


&nbsp;   
### 10-17 ~ 之后

- [ValueType & SequenceNumber](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/ValueType%20%26%20SequenceNumber%202018-11-07%20-%20rsy.md):任思远
  - `db/dbformat.h`
  - 指导:
      - `put` 插入新的 kv 数据;`delete` 是 `put` 空,为了区分真实 kv 数据和删除操作的 mock 数据,使用 `ValueType` 来标识
      - leveldb 中的每次更新(`put`/`delete`)操作都拥有一个版本,由 `SequnceNumber` 来标识,整个 db 有一个全局值保存着当前使用到的 `SequnceNumber`
  - 总结:
- [Snapshot](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Snapshot%20-%202018-11-07%20-%20rsy.md):任思远
  - `include/leveldb/snapshot.h`
  - 总结:依赖于 SequnceNumber 来标识时间点
- [Manifest & VersionEdit](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Manifest%20%26%20VersionEdit%20-%202018-11-07%20-%20rsy.md):任思远
  - `db.version_set.cc`, `db/version_edit.h`, `db/version_edit.cc`
  - 总结:Manifest 文件保存元信息数据,VersionEdit 是 Version 的增量
- [Log](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Log%20-%202018-11-08%20-%20rsy.md):任思远
  - `db/log_format.h`, `db/log_reader.h`, `db/log_reader.cc`, `db/og_writer.h`, `db/log_writer.cc`
  - 总结:`DBImpl::Write()` 中先写 log,然后更新 memtable
- [Recover](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Recover%20-%202018-11-08%20-%20rsy.md):任思远
  - `db/version_set.h`, `db/version_set.cc`, `db/db_impl.h`, `db/db_impl.cc`
  - 总结:数据库每次启动时,都会有一个 recover 的过程,简要地来说,就是利用 Manifest 信息重新构建一个最新的 version
- [Version & VersionSet](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Version%20%26%20VersionSet%20-%202018-11-12%20-%20rsy.md):任思远
  - `db/version_set.h`, `db/version_set.cc`
  - 总结:
      - Version 是管理某个版本的所有 sstable 的类,就其导出接口而言,无非是:
          - 遍历 sstable
          - 查找 k/v
          - 为 compaction 做些事情
          - 给定 range,检查重叠情况
      - VersionSet 管理整个 db 的当前状态,负责包括 Log, Compaction, Recover 等
- [DBImpl](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/DBImpl%20-%202018-11-14%20-%20rsy.md):任思远
  - `include/leveldb/db.h`, `db/db_impl.h`, `db_impl.cc`
  - 总结:
      - 实现了所有面对用户的接口
      - 负责 Recover,Log,Compaction 等

================================================
FILE: doc/文档格式模板.md
================================================
# Module - 2018-mm-dd xxx

- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [内部实现细节](#inner_detail)
- [参考资料](#reference)


&nbsp;   
<a id="module_info"></a>
## 模块信息



&nbsp;   
<a id="module_in_brief"></a>
## 模块概要


&nbsp;   
<a id="module_function"></a>
## 模块功能



&nbsp;   
<a id="interface_specification"></a>
## 接口说明




&nbsp;   
<a id="dependency_specification"></a>
## 相关依赖说明



&nbsp;   
<a id="inner_detail"></a>
## 内部实现细节



&nbsp;   
<a id="reference"></a>
## 参考资料



================================================
FILE: doc/文档格式说明.md
================================================
# 项目文档格式说明
-----

文中所有非引用部分均为格式要求部分。引用部分为格式要求的解释。(此句除外)  

### 文档样例:[]()

-----

>所有文档均采用md格式,推荐使用编辑器 MarkdownPad2 / Typora  
>所有图片均应放置于与文档同目录下的 `assets` 目录下   


- xxx 编辑于 2018-09-16
  - ...
  - 123
- xxx 编辑于 2018-09-17
  - asd

>文首应记录 **编辑者**,**编辑时间**,**编辑内容**    
>若不同成员欲修改同一文档,事前在群里商量好    


- [模块信息](#module_info)
- [模块概要](#module_in_brief)
- [模块功能](#module_function)
  - [存储部分](#storage)
  - [压缩部分](#compress)
  - [日志部分](#log)
- [接口说明](#interface_specification)
- [相关依赖说明](#dependency_specification)
- [其他](#other)

>除非相关模块过于简单、朴素,像   [Slice](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Slice%20-%202018-09-16%20-%20rsy.md)   
>否则应写出简要链接和导向来帮助阅读


<a id="module_info"></a>
## 模块信息

Slice.h (include/leveldb/slice.h)  
对字符串指针做了简单的封装。  

>信息包括 **文件名**,**路径名**(所有路径均相对于 google/leveldb),**介绍** 等。

<a id="module_in_brief"></a>
## 模块概要

为操作数据的方便,将数据和长度包装成 Slice 使用,直接操控指针避免不必要的数据拷贝。

概括地讲:`leveldb::Slice` 是个轻量级的 `std::string`.

>一两句话介绍模块


<a id="module_function"></a>
## 模块功能

![Slice_09_16](../architecture/DB/assets/Slice_09_16.png)

Specification:

1. 所有操作均有边界检查。
2. `void remove_prefix(size_t n)`:向后移动 n 个byte, size 减少 n.
3. `bool starts_with(const Slice& x)`:判断前缀是否与 x 吻合。


>最好有UML类图(可以使用Understand自动生成),**图片名称应为内容+日期**(避免混乱,比如这张图叫做 Slice_09_16.png)   
>核心部分还可以附加流程图说明   
>所有图片均应放置于与文档同目录下的 `assets` 目录下   
>最好有解释,大部分内容头文件中都有,把重要的接口描述清楚   
>实现文件的内容可以不用出现,除非有核心功能的组件  

<a id="storage"></a>
### 存储部分

<a id="compress"></a>
### 压缩部分

<a id="log"></a>
### 日志部分


<a id="interface_specification"></a>
## 接口说明

>说明接口的功能,主要在什么地方使用     
>
>某些接口可能会*改变系统状态*(不那么local的状态,即比较全局的状态)   
>对于这样的接口,**必须**描述清楚:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   
>**调用接口前系统的状态**,保证进入模块时所有环境是正常的(怎样算正常,如某些参数要有什么样的保证!!!比如全局状态,环境变量,错误处理函数,日志文件,缓存一致等,,,尤其全局状态之间可能存在复杂的联系)   
>**调用接口后系统的状态**,即调用完成后保证这些变量处于什么样的状态!!!!!!(还是同一批变量)     
>
>注意到一个模块调用前的状态,很可能是另一个模块调用后造成的(即整个流程对于全局状态的修改有着怎样的逻辑关系,以及是怎样维护这些状态的),可以对此做一些探究并给出说明


&nbsp;    
&nbsp;    
&nbsp;    

>上述部分,如**模块概要**,**模块功能**,**接口说明**,若差别不大,则可以不用分得这么细,只要把模块理清楚就行  

&nbsp;    


<a id="dependency_specification"></a>
## 相关依赖说明

>描述该模块用于何处,表现出何功能   


<a id="other"></a>
## 其他

>对于一些实现技巧,架构手法,设计理念,各种设计模式的补充说明   
>例如 C++ 中常见的有 RAII, copy-and-swap, erase-remove, pImpl, 和各种 pattern 等   
>使用了什么数据结构和算法,优越性在哪里,是否有其他的实现方式,**trade-off** ???    
>参考的文献



================================================
FILE: doc/第十四组 - 进度报告.md
================================================
# 第十四组 - 进度报告

&nbsp;   
**软件体系架构恢复**:

- [总体架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E8%BD%AF%E4%BB%B6%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.md)
  - [数据库逻辑架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/README.md) ![](https://img.shields.io/badge/leveldb--db-25%25-orange.svg)
  - [底层存储架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/README.md) ![](https://img.shields.io/badge/leveldb--table-70%25-yellow.svg)
  - [基础工具库](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/README.md) ![](https://img.shields.io/badge/leveldb--util-70%25-blue.svg)
  - [其他:平台相关-锁-信号-原子-压缩](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/%E5%B9%B3%E5%8F%B0%E7%9B%B8%E5%85%B3-%E9%94%81-%E4%BF%A1%E5%8F%B7-%E5%8E%9F%E5%AD%90-%E5%8E%8B%E7%BC%A9/README.md) 

-----


&nbsp;   
### 09-19 ~ 09-21

<details><summary>查看本周具体工作</summary>

全组成员安装 understand 源码阅读软件,阅读 [文档格式说明](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E6%96%87%E6%A1%A3%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E.md)    
阅读 [参考资料](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/README.md),了解 `leveldb` 的大概结构。   

</details>
&nbsp;   

安装源码阅读工具,并了解 leveldb。


&nbsp;   
### 09-22 ~ 09-24

<details><summary>查看本周具体工作</summary>

因为是第一次,又是中秋节,就少布置一些任务。   

1. 阅读 [这篇文档](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/Understand%E9%85%8D%E7%BD%AE%E5%8F%8A%E4%BD%BF%E7%94%A8/README.md),配置及使用 understand.
2. 阅读下面4篇文章,了解 `leveldb` 的整体构架。

<a></a>

- [LevelDB 源码分析:主体结构](http://cighao.com/2016/08/14/leveldb-source-analysis-02-structure/)  
- [LevelDB实现总结](http://morefreeze.github.io/2016/05/LevelDB-Summarize.html)
- [我的leveldb总结](https://blog.csdn.net/poi7777/article/details/49124491)
- [LevelDB详解](https://blog.csdn.net/linuxheik/article/details/52768223)

</details>
&nbsp;   

配置及使用源码阅读工具 Understand,并阅读资料,理解 leveldb 的整体架构。


&nbsp;   
### 09-26 ~ 10-02 第五周

<details><summary>查看本周具体工作</summary>

从一些基础工具库开始读起。

- [Status]():杜致
  - `util/status.cc`, `include/leveldb/status.h`, 文档放置于 "DB" 文件夹下
  - 可参考:
      - [leveldb源码分析之Status](http://luodw.cc/2015/10/15/leveldb-03/)
  - 总结:返回状态,封装了错误号和错误信息,统一进行处理
- [Options, Env]():季宇恒
  - `util/options.cc`, `include/leveldb/options.h`, `util/env.cc`, `include/leveldb/env.h`, 文档名叫 "Options.md" 和 "Env.md"
  - 指导:抽象了整个 db 的环境与配置选项,主要搞清楚接口与参数的说明
  - 可参考:
      - [leveldb](https://dirtysalt.github.io/html/leveldb.html)
      - [文件系统及Env-leveldb源码剖析(5)](http://www.pandademo.com/2016/03/file-system-and-env-leveldb-source-dissect-5/)
  - 总结:
- [Comparator]():毛凯
  - `util/comparator.cc`, `include/leveldb/comparator.h`, `db/dbformat.h`, `db/dbformat.cc`
  - 指导:`Comparator` 下主要有两种实现:`BytewiseComparatorImpl`, `InternalKeyComparator`,区分两者的 **功能**,以及 **分别在什么情况下使用**
  - 总结:
- [ParsedInternalKey, InternalKey, LookupKey](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/dbformat_key-2018-10-01-ss.md):苏胜
  - `db/dbformat.h`, `db/dbformat.cc`, 文档名叫 "dbformat_key - 2018-10-01 - ssh.md", 文档放置于 "DB" 文件夹下
  - 指导:db 内部包装的 key 结构,需要阐释清楚 **具体结构**,**含义**,**用途** 以及 **依赖于key的操作**
  - 可参考:[leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
  - 总结:对 key 的包装,内含 `user_key`, `SequenceNumber`, `ValueType`
- [Slice](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Slice%20-%202018-09-16%20-%20rsy.md):任思远
  - `include/leveldb/slice.h`
  - 总结:为操作数据的方便,将数据和长度包装成 Slice 使用,直接操控指针避免不必要的数据拷贝。就是个简易的 **`string`**
- [Arena](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/Arena/arena%20-%202018-09-30%20-%20rsy.md):任思远
  - `util/arena.h`, `util/arena.cc`
  - 总结:Arena 主要与 MemTable 关联使用,实际主要用于 SkipList 中的 Node 内存分配,统一 MemTable 的内存分配需求,减少内存分配的实际系统调用次数(尤其针对小块内存)
- [Coding](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/Coding/coding%20-%202018-09-06%20-%20rsy.md):任思远
  - `util/coding.h`
  - 总结:用于压缩 int 所占的存储空间的一套编解码工具
- [Cache](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/util/LRU/cache%20-%202018-09-20%20-%20rsy.md):任思远
  - `include/leveldb/cache.h`, `util/cache.cc`
  - 总结:在 TableCache 用于缓存 SSTable 的对应索引元数据
- [TableCache](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/TableCache%20-%202018-09-30%20-%20rsy.md):任思远
  - `db/table_cache.h`, `db/table_cache.cc`
  - 总结:`TableCache`缓存的是 sstable 的索引数据,k-v格式为 `file_number` - `TableAndFile`
- [Iterator](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Iterator%20-%202018-10-01%20-%20rsy.md):任思远
  - `include/leveldb/iterator.h`, `table/iterator.cc`
  - 总结:leveldb 中对 key 的查找和遍历,上层统一使用 `Iterator` 的方式处理,屏蔽底层的处理,统一逻辑。 提供 `RegisterCleanup()` 可以在 `Iterator` 销毁时,做一些清理工作(比如释放 `Iterator` 持有句柄的引用)
- [WriteBatch](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/WriteBatch%20-%202018-10-01%20-%20rsy.md):任思远
  - `include/leveldb/write_batch.h`, `db/write_batch.cc`
  - 总结:leveldb 内部的一个批量写的结构,在 leveldb 为了提高插入和删除的效率,在其插入过程中都采用了批量集合相邻的多个具有相同同步设置的写请求以批量的方式进行写入。`WriteBatch` 是一个连续字节流的表示,是将所有的请求序列化称为连续的字节流
- ![](https://img.shields.io/badge/leveldb--table-1%25-lightgrey.svg) ![](https://img.shields.io/badge/leveldb--util-30%25-yellow.svg)

</details>
&nbsp;   

阅读 leveldb 中 util 模块,并总结出用途及依赖关系。   
[util 模块总结](https://github.com/rsy56640/read_and_analyse_levelDB/tree/master/architecture/util)


&nbsp;   
### 10-03 ~ 10-09 第六周(演讲: 10-10)

<details><summary>查看本周具体工作</summary>

这周看一些有趣的构件。

- [BloomFilter]():毛凯
  - `util/bloom.cc`
  - 指导:布隆过滤器的**效果**,**缺点** 以及 **何时刷新**(我猜的,可能不)
  - 可参考:
      - [BloomFilter - 基本理论](https://www.jianshu.com/p/181f40047834)
      - [布隆过滤器的原理和实现](https://github.com/cpselvis/zhihu-crawler/wiki/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E5%AE%9E%E7%8E%B0)
  - 总结:
- [Logging]():季宇恒
  - `util/logging.h`, `util/logging.cc`
  - 可参考:
  - 总结:
- [skiplist]():苏胜
  - `db/skiplist.h`, 放置于 `util/memtable` 文件夹下,这个给 **2周** 时间来看
  - 指导:跳表是工程中常见的数据结构,用于快速的kv查找,优点是比各种 BST(AVL,红黑树)易于实现,而且效率也高。主要思想是:如何在链表中维护 “某种结构” 以获得类似二分查找的性质
  - 可参考:
      - [leveldb源码分析之Skiplist](http://luodw.cc/2015/10/16/leveldb-05/)
      - [leveldb-handlebook](https://leveldb-handbook.readthedocs.io/zh/latest/memorydb.html#id2)
      - [leveldb](https://www.cnblogs.com/xueqiuqiu/tag/leveldb/)
      - [深夜学算法之SkipList:让链表飞](https://www.jianshu.com/p/fcd18946994e)
      - [leveldb(五):SkipList跳表](https://blog.csdn.net/weixin_36145588/article/details/76393448)
- [FileNumber, FileName]():杜致
  - `db/filename.h`, `db/filename.cc`, `db/dbformat.h`
  - 指导:
      - db 创建文件时会按照规则将 `FileNumber` 加上特定后缀作为文件名。 所以, 运行时只需要记录 `FileNumber`(uint64_t)即可定位到具体的文件路径,省掉了字符串的麻烦。 `FileNumber` 在 db 中全局递增
      - db 中的文件用文件名区分类型
  - 可参考:
      - [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
      - [LevelDB源码解析11.文件序号](https://zhuanlan.zhihu.com/p/35343043)
  - 总结:
- [BlockHandle, Footer](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/BlockHandle%26Footer%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/format.h`, `table/format.cc`
  - 总结:
      - `BlockHandle` 封装了 `Block` 的元信息(位于 sstable 的 offset/size)
      -  `Footer` 用来存储 `meta index block` 与 `index block` 在 sstable 中的索引信息,另外尾部还会存储一个 `magic word`。落盘和读取时会调用相关接口
      - `ReadBlock()` 从随机文件里面读取 `Block` 出来,对于这个 `Block` 的位置的话由 `handle` 提供(注意每个 `Block` 后面还有 `crc` 和 `type`)
- [FilterPolicy](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/FilterPolicy%20-%202018-10-03%20-%20rsy.md):任思远
  - `include/leveldb/filter_policy.h`
  - 总结:上层用于查找 key,跳过一定没有出现 key 的 sstable
- [FilterBolck](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/FilterBlock%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/filter_block.h`, `table/filter_block.cc`
  - 总结:`filter block` 就是 `meta block`,用来存储一些过滤器相关的数据。把要输出的结果按照想要的格式整理好,在内存中放置好。之后由 `log_writer` 写入文件(`BlockBuilder` 同理)。并且提供 `Reader` 来查找可能存在的 `key`
- [Block](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Block%20-%202018-10-02%20-%20rsy.md):任思远
  - `table/block.h`, `table/block.cc`, `table/block_builder.h`, `table/block_builder.cc`
  - 总结:存储数据,通过 `BlockHandle` 获取 `Block`(offset 和 size),并封装了 `Block::Iter` 用于上层调用,由 `BlockBuilder` 创建
- [IteratorWrapper](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Iterator_Wrapper%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/iterator_wrapper.h`
  - 总结:缓存了 `valid`, `key`,并管理 iter 资源
- [TwolevelIterator](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/TwoLevelIterator%20-%202018-10-03%20-%20rsy.md):任思远
  - `table/two_level_iterator.h`, `table/two_level_iterator.cc`
  - 总结:将对 `Table` 的遍历封装,对外展现如同线性遍历。
- [Builder](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Builder%20-%202018-10-03%20-%20rsy.md):任思远
  - `db/builder.h`, `db/builder.cc`
  - 总结:`BuildTable()`:为 level-0 创建 `Table`,并把 metadata 存到 `FileMetaData* meta`
- [Memtable](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/Memtable%20-%202018-10-04%20-%20rsy.md):任思远
  - `db/memtable.h`, `db/memtable.cc`
  - 总结:存储 k-v 插入、删除的操作(并不真正删除,只打标记)并且将数据序列化,在写 level-0 时将 `iter` 传入 `BuildTable` 写入文件
- [Table]():任思远
  - `include/leveldb/table.h`, `table/table.cc`, `table/table_builder.cc`, `include/leveldb/table_builder.h`
  - 总结:
      - `Table` 类主要完成 sstable 的读取逻辑
      - `TableBuilder` 类用于构建 sstable,将 `data block`, `filter block`, `meta index block`,  `index block`, `footer` 写入文件
- [Merge](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/MergingIterator%20-%202018-10-05%20-%20rsy.md):任思远
  - `table/merge.h`, `table/merge.cc`
  - 总结:多路 Iterator 归并称为一个 Iterator 进行遍历,用于 `DBImpl::NewInternalIterator()` 中收集所有 iterator(memtable, imm memtable, sstable)然后统一处理
- [Compaction](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/Compaction%20-%202018-10-05%20-%20rsy.md):任思远
  - `db/version_set.h`, `db/version_set.cc`
  - 总结:compaction 是执行 LSM-tree 中 merge 的过程
      - minor compaction 用于内存到外存的迁移过程
      - major compaction 用于 level 之间的迁移
- [LSM](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/LSM%20-%202018-10-06%20-%20rsy.md):任思远
  - 总结:放弃磁盘读性能来换取写的顺序性
- ![](https://img.shields.io/badge/leveldb--db-25%25-blue.svg) ![](https://img.shields.io/badge/leveldb--table-70%25-orange.svg) ![](https://img.shields.io/badge/leveldb--util-70%25-ff69b4.svg)

</details>
&nbsp;   

理解存储模块是如何工作的,包括:

- 内存存储
- 内存 dump 到外存的过程 —— Minor Compaction
- 外存存储 —— 顺序写+时间戳 策略
- SSTable 的 Major Compaction

[内存存储总结](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/DB/README.md)   
[外存存储总结](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/architecture/SSTable/README.md)   


&nbsp;   
### 10-03 ~ 10-09 第六周

本周主要是[SA文档](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md)。

<details><summary>查看本周具体工作</summary>

[SA文档](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md):

- 任思远:[Context View](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#Context_View), [Functional View](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#Functional_View), [Architecture](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#Architecture)
- 苏胜:[Evolution Perspective](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#Evolution_Perspective)
- 毛凯:[Technical Debt Analysis](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#7-technical-debt-analysis)
- 杜致:[Stakeholders](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#Stakeholders)
- 季宇恒:[Introduction](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md#Introduction)


</details>
&nbsp;   


&nbsp;   
### 10-24 ~ 11-06

本周主要是完善[SA文档](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/SA.md)。

<details><summary>查看本周具体工作</summary>

- 任思远:`Architecture`
- 苏胜:`Conclusion`
- 毛凯:`Evolution Perspective`,发展历史
- 杜致:[Stakeholders - levelup - A node.js wrapper for abstract-leveldown compliant stores](https://github.com/Level/levelup)
  - 主要谈一谈 leveldb 在js里使用的感受,有什么方便或者劣势。另外还有levelup社区的情况。
  - 可参考:[Intro to LevelDB](https://www.youtube.com/watch?v=sR7p_JbEip0)
- 季宇恒:`Technical Debt Analysis - Defect Debt`

</details>
&nbsp;   




================================================
FILE: doc/软件体系结构.md
================================================
# LevelDB

-----

![](assets/leveldb_icon_09_26.png)

LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.   

![](https://img.shields.io/badge/database-C%2B%2B-green.svg)


![](assets/leveldb_UML_10_01.png)

![](assets/leveldb_UML2_10_01.png)



================================================
FILE: reference/README.md
================================================
# Reference

- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/DB%20leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf)
- [leveldb-handbook](https://leveldb-handbook.readthedocs.io/zh/latest/)
- [LevelDB: Read the Fucking Source Code.](http://www.grakra.com/2017/06/17/Leveldb-RTFSC/)
- [leveldb - 超全讲解..........](https://dirtysalt.github.io/html/leveldb.html)
- [LevelDB源码分析 - 百度文库 100多页..................](https://wenku.baidu.com/view/b3285278b90d6c85ec3ac687.html)
- [The principle of LevelDb analysis - 英文,很多文章的出处](https://www.programering.com/a/MjMyMTNwATQ.html)
- [Leveldb代码阅读笔记 - codedump](https://www.codedump.info/post/20190215-leveldb/)
- [存储引擎技术架构与内幕 (leveldb-1) #58](https://github.com/abbshr/abbshr.github.io/issues/58)
- [浅析 Bigtable 和 LevelDB 的实现](https://draveness.me/bigtable-leveldb)
- [LevelDB实现总结](http://morefreeze.github.io/2016/05/LevelDB-Summarize.html)
- [吉姆餐厅 - 知乎专栏](https://zhuanlan.zhihu.com/jimderestaurant)
- [CodeIt - 知乎专栏](https://zhuanlan.zhihu.com/codeit)
- [罗道文的私房菜 - leveldb源码分析 - 专栏](http://luodw.cc/categories/leveldb/)
- [leveldb源码阅读和重写,重写的过程中加上注释方便理解。 - on Github](https://github.com/snipercy/leveldb)
- [Leveldb源码分析 - 专栏](https://blog.csdn.net/sparkliang/article/category/1342001)
- [Analysis leveldb source code step by step - Github](https://github.com/rjl493456442/leveldb-handbook)
- [PandaDemo - levelDB - 专栏](http://www.pandademo.com/category/tech/leveldb/)
- [灿哥哥的博客 - leveldb源码分析 - 专栏](https://blog.csdn.net/caoshangpa/article/category/7351350)
- [【Swartz2015的专栏】leveldb源码剖析 - 专栏](https://blog.csdn.net/swartz2015/article/category/6800179)
- [leveldb 源码学习 - 专栏](https://www.jianshu.com/nb/9496943)
- [LevelDB源码剖析 - 专栏](http://mingxinglai.com/cn/categories/#NoSql)
- [leveldb - zhaohai-shen - 专栏](https://www.cnblogs.com/shenzhaohai1989/p/)
- [leveldb - tgates - 专栏](https://www.cnblogs.com/KevinT/category/590804.html)
- [巴山独钓 - leveldb源码分析](https://blog.csdn.net/tankles/article/category/1167229)
- [庖丁解LevelDB](http://catkang.github.io/2017/01/07/leveldb-summary.html)
- [leveldb使用和源码分析 - 小专栏](https://www.cnblogs.com/ym65536/category/1076262.html)
- [weixin_36145588的博客 - leveldb - 小专栏](https://blog.csdn.net/weixin_36145588/article/category/6995299)
- [leveldb源码学习系列 - 小专栏](https://www.cnblogs.com/Jack47/tag/leveldb/)
- [levelDB源码解析 - 小专栏](https://www.jianshu.com/nb/10717873)
- [Leveldb官方文档(中文版)](https://blog.csdn.net/u012796139/article/details/50241409)
- [Leveldb官方文档 - on Github](https://github.com/google/leveldb/tree/master/doc)
- [leveldb资料整理](http://hideto.iteye.com/blog/1328921)
- [LevelDB详解](https://blog.csdn.net/linuxheik/article/details/52768223)
- [LevelDb日知录之一:LevelDb 101](http://blog.chinaunix.net/uid-26575352-id-3245476.html)
- [深入leveldb-初步认识leveldb](https://blog.csdn.net/chenguolinblog/article/details/50611767)
- [我的leveldb总结](https://blog.csdn.net/poi7777/article/details/49124491)
- [Blog of Finesse - 小专栏](http://brg-liuwei.github.io/)
- [levelDB 小专栏](https://www.cnblogs.com/xueqiuqiu/tag/leveldb/)
- [ON TEH WAY - 小专栏](http://tonyz93.blogspot.com/search?q=leveldb)
- [专栏 - SSTable - Compaction](http://bean-li.github.io/categories/#leveldb)
- [LevelDB关键实现图解](http://www.wzxue.com/leveldb%E5%9B%BE%E8%A7%A3/)
- [【LevelDB源码剖析系列】SkipList与Memtable](https://yq.aliyun.com/articles/64357)
- [LevelDB系统结构与设计思路分析](https://yq.aliyun.com/articles/618109?spm=a2c4e.11154873.tagmain.54.48c636ddEneOSc)
- [随笔分类 - leveldb - 专栏](https://www.cnblogs.com/KevinT/category/590804.html)
- [数据分析与处理之二(Leveldb 实现原理)](http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html?spm=a2c4e.11153940.blogcont618109.11.5e1562979sTGQC)
- [LevelDB 实现分析](http://taobaofed.org/blog/2017/07/05/leveldb-analysis/)
- [Yong's Blog - 源码剖析,英文注释](http://yongblog.us/tags/LevelDB/)
- [LSM 算法的原理是什么? - 知乎](https://www.zhihu.com/question/19887265)
- [一文带你了解LSM Compaction](https://zhuanlan.zhihu.com/p/37003275)
- [Log Structured Merge Trees](http://www.benstopford.com/2015/02/14/log-structured-merge-trees/)
- [日志结构的合并树原理](https://www.cnblogs.com/siegfang/archive/2013/01/12/lsm-tree.html)
- [SSTable and Log Structured Storage: LevelDB](https://www.igvita.com/2012/02/06/sstable-and-log-structured-storage-leveldb/)
- [LevelDB之源码探析小结](http://zouzls.github.io/2016/12/06/LevelDB%E4%B9%8B%E6%BA%90%E7%A0%81%E6%8E%A2%E6%9E%90%E5%B0%8F%E7%BB%93/)
- [LevelDB之LSM-Tree](http://zouzls.github.io/2016/11/23/LevelDB%E4%B9%8BLSM-Tree/)
- [详解SSTable结构和LSMTree索引](http://www.cnblogs.com/fxjwind/archive/2012/08/14/2638371.html)
- [Log Structured Merge Trees译文以及LSM调研心得](http://weakyon.com/2015/04/08/Log-Structured-Merge-Trees.html)
- [LevelDb日知录 - 全](https://blog.csdn.net/elton_xiao/article/details/52556851)
- [详解SSTable结构和LSMTree索引](https://yq.aliyun.com/articles/85107)
- [分布式存储SStable与LSM Tree](http://coterie.tech/2017/03/24/sstable-lsmtree/)
- [LSM树(Log-Structured Merge Tree)存储引擎](https://blog.csdn.net/u014774781/article/details/52105708)
- [levelDB 源码分析(四):Arena](http://www.liuhaihua.cn/archives/381569.html)
- [leveldb源码分析(1)--arena内存池的实现](https://segmentfault.com/a/1190000003061165)
- [小专栏](http://plutolove.hatenablog.com/archive/category/LevelDB)
- [leveldb 源碼分析 —— SkipList跳錶](https://hk.saowen.com/a/a2139d2f9c67a8fc429c49c61ab1af46887140473d55ffbea63a9d9780136739)
- [Reviewing LevelDB - 20篇文章](https://ayende.com/blog/search?q=leveldb)
- [LevelDB 源码分析 - 专栏 7篇文章](http://cighao.com/2016/08/13/leveldb-source-analysis-01-introduction/)



&nbsp;   
&nbsp;   



B+ & LSM:

- [(转)B+树 LSM 树 COLA树 原理及在海量存储中的应用](https://blog.csdn.net/anderscloud/article/details/7181085)
- [大数据索引技术 - B+ tree vs LSM tree](https://www.cnblogs.com/fxjwind/archive/2012/06/09/2543357.html)
- [数据库如何抵抗随机IO:问题、方法与现实](http://wangyuanzju.blog.163.com/blog/static/13029201132154010987)



&nbsp;   
&nbsp;   



其他:

- [LSM-Tree 与 RocksDB](http://www.importnew.com/28083.html)
- [Workload Analysis of Key-Value Stores on Non-Volatile Media](https://www.snia.org/sites/default/files/SDC/2017/presentations/Storage_Architecture/Verma_Vishal_Gohad_Tushar_Workload_Analysis_of_Key-Value_Stores_on_Non-Volatile_Media.pdf)
- [LevelDB From Scratch in Java](https://medium.com/@wishmithasmendis/leveldb-from-scratch-in-java-c300e21c7445)
- [From RDBMS to Key-Value Store: Data Modeling Techniques](https://medium.com/@wishmithasmendis/from-rdbms-to-key-value-store-data-modeling-techniques-a2874906bc46)
- [LSM tree: why bother?](https://medium.com/@mlowicki/lsm-tree-why-bother-9da9be0b307)
Download .txt
gitextract_jqr4w8rl/

├── README.md
├── architecture/
│   ├── DB/
│   │   ├── Builder - 2018-10-03 - rsy.md
│   │   ├── DBImpl - 2018-11-14 - rsy.md
│   │   ├── DBIter - 2018-11-14 - rsy.md
│   │   ├── Log - 2018-11-08 - rsy.md
│   │   ├── Manifest & VersionEdit - 2018-11-07 - rsy.md
│   │   ├── Memtable - 2018-10-04 - rsy.md
│   │   ├── README.md
│   │   ├── Recover - 2018-11-08 - rsy.md
│   │   ├── Slice - 2018-09-16 - rsy.md
│   │   ├── Snapshot - 2018-11-07 - rsy.md
│   │   ├── Status-2018-10-02-dz.md
│   │   ├── TableCache - 2018-09-30 - rsy.md
│   │   ├── ValueType & SequenceNumber 2018-11-07 - rsy.md
│   │   ├── Version & VersionSet - 2018-11-12 - rsy.md
│   │   ├── WriteBatch - 2018-10-01 - rsy.md
│   │   └── dbformat_key-2018-10-01-ss.md
│   ├── SSTable/
│   │   ├── Block - 2018-10-02 - rsy.md
│   │   ├── BlockHandle&Footer - 2018-10-03 - rsy.md
│   │   ├── Compaction - 2018-10-05 - rsy.md
│   │   ├── FilterBlock - 2018-10-03 - rsy.md
│   │   ├── FilterPolicy - 2018-10-03 - rsy.md
│   │   ├── Iterator - 2018-10-01 - rsy.md
│   │   ├── Iterator_Wrapper - 2018-10-03 - rsy.md
│   │   ├── LSM - 2018-10-06 - rsy.md
│   │   ├── MergingIterator - 2018-10-05 - rsy.md
│   │   ├── README.md
│   │   ├── Table - 2018-10-04 - rsy.md
│   │   └── TwoLevelIterator - 2018-10-03 - rsy.md
│   ├── util/
│   │   ├── Arena/
│   │   │   └── arena - 2018-09-30 - rsy.md
│   │   ├── BloomFilter/
│   │   │   └── BloomFilter - 2018-10-10 mk.md
│   │   ├── Coding/
│   │   │   └── coding - 2018-09-06 - rsy.md
│   │   ├── Comparator/
│   │   │   └── Comparator - 2018-10-10 - mk.md
│   │   ├── Env_Options/
│   │   │   ├── Env - 2018-10-04 -jyh.md
│   │   │   ├── Options - 2018-10-04 - jyh.md
│   │   │   └── README.md
│   │   ├── LRU/
│   │   │   └── cache - 2018-09-20 - rsy.md
│   │   ├── Logging/
│   │   │   ├── Logging - 2018-10-14 - jyh.md
│   │   │   └── README.md
│   │   ├── README.md
│   │   ├── memenv/
│   │   │   └── README.md
│   │   └── memtable/
│   │       ├── README.md
│   │       └── skiplist_2018_10_12_ss.md
│   └── 平台相关-锁-信号-原子-压缩/
│       ├── README.md
│       ├── atomic/
│       │   └── README.md
│       ├── posix/
│       │   └── README.md
│       └── snappy/
│           └── README.md
├── doc/
│   ├── SA.md
│   ├── Understand配置及使用/
│   │   └── README.md
│   ├── plan.md
│   ├── 文档格式模板.md
│   ├── 文档格式说明.md
│   ├── 演讲10-10.pptx
│   ├── 演讲11-21.pptx
│   ├── 第十四组 - 进度报告.md
│   └── 软件体系结构.md
└── reference/
    ├── LevelDB源码分析.docx
    └── README.md
Condensed preview — 58 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (256K chars).
[
  {
    "path": "README.md",
    "chars": 40,
    "preview": "# read_and_analyse_levelDB\nLevelDB 源码剖析\n"
  },
  {
    "path": "architecture/DB/Builder - 2018-10-03 - rsy.md",
    "chars": 980,
    "preview": "# Builder - 2018-10-03 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [相关依赖说明](#dependency_specification)\n- [内"
  },
  {
    "path": "architecture/DB/DBImpl - 2018-11-14 - rsy.md",
    "chars": 17281,
    "preview": "# DBImpl - 2018-11-14 - rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [接口说明](#interface_specification)\n- [相关依"
  },
  {
    "path": "architecture/DB/DBIter - 2018-11-14 - rsy.md",
    "chars": 1406,
    "preview": "# DBIter - 2018-11-14 - rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [接口说明](#interface_specification)\n- [相关依"
  },
  {
    "path": "architecture/DB/Log - 2018-11-08 - rsy.md",
    "chars": 4400,
    "preview": "# Log - 2018-11-08 - rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interfa"
  },
  {
    "path": "architecture/DB/Manifest & VersionEdit - 2018-11-07 - rsy.md",
    "chars": 4081,
    "preview": "# Manifest & VersionEdit - 2018-11-07 - rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function"
  },
  {
    "path": "architecture/DB/Memtable - 2018-10-04 - rsy.md",
    "chars": 3325,
    "preview": "# Memtable - 2018-10-04 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#inte"
  },
  {
    "path": "architecture/DB/README.md",
    "chars": 7299,
    "preview": "- [Status](#status)\n- [KeyFormat](#key_format)\n- [WriteBatch](#write_batch)\n- [TableCache](#table_cache)\n- [Memtable](#m"
  },
  {
    "path": "architecture/DB/Recover - 2018-11-08 - rsy.md",
    "chars": 1953,
    "preview": "# Recover - 2018-11-07 - rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [参考资料](#ref"
  },
  {
    "path": "architecture/DB/Slice - 2018-09-16 - rsy.md",
    "chars": 322,
    "preview": "# Slice - 2018-09-16 rsy\n\nSlice (include/leveldb/slice.h) \n\n为操作数据的方便,将数据和长度包装成 Slice 使用,直接操控指针避免不必要的数据拷贝。\n\n概括地讲:`leveldb"
  },
  {
    "path": "architecture/DB/Snapshot - 2018-11-07 - rsy.md",
    "chars": 2124,
    "preview": "# Snapshot - 2018-11-07 - rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#in"
  },
  {
    "path": "architecture/DB/Status-2018-10-02-dz.md",
    "chars": 1685,
    "preview": "# Status-18-10-02 杜致\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interface_s"
  },
  {
    "path": "architecture/DB/TableCache - 2018-09-30 - rsy.md",
    "chars": 2731,
    "preview": "# TableCache - 2018-09-30 rsy\n\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#i"
  },
  {
    "path": "architecture/DB/ValueType & SequenceNumber 2018-11-07 - rsy.md",
    "chars": 1723,
    "preview": "# ValueType & SequenceNumber - 2018-11-07 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [参考资料](#reference)\n\n\n"
  },
  {
    "path": "architecture/DB/Version & VersionSet - 2018-11-12 - rsy.md",
    "chars": 6814,
    "preview": "# Version & VersionSet - 2018-11-12 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- "
  },
  {
    "path": "architecture/DB/WriteBatch - 2018-10-01 - rsy.md",
    "chars": 3456,
    "preview": "# WriteBatch - 2018-10-01 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#in"
  },
  {
    "path": "architecture/DB/dbformat_key-2018-10-01-ss.md",
    "chars": 3195,
    "preview": "# Module - 2018-10-01 苏胜\r\n\r\n- [模块信息](#module_info)\r\n- [模块概要](#module_in_brief)\r\n- [模块功能](#module_function)\r\n- [接口说明](#in"
  },
  {
    "path": "architecture/SSTable/Block - 2018-10-02 - rsy.md",
    "chars": 3087,
    "preview": "# Block - 2018-10-02 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interfa"
  },
  {
    "path": "architecture/SSTable/BlockHandle&Footer - 2018-10-03 - rsy.md",
    "chars": 2376,
    "preview": "# BlockHandle & Footer - 2018-10-03 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [接口说明](#interface_specifica"
  },
  {
    "path": "architecture/SSTable/Compaction - 2018-10-05 - rsy.md",
    "chars": 4123,
    "preview": "# Compaction - 2018-10-05 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#in"
  },
  {
    "path": "architecture/SSTable/FilterBlock - 2018-10-03 - rsy.md",
    "chars": 2281,
    "preview": "# FilterBlock - 2018-10-03 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#i"
  },
  {
    "path": "architecture/SSTable/FilterPolicy - 2018-10-03 - rsy.md",
    "chars": 1067,
    "preview": "# FilterPolicy - 2018-10-03 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#"
  },
  {
    "path": "architecture/SSTable/Iterator - 2018-10-01 - rsy.md",
    "chars": 2226,
    "preview": "# Iterator - 2018-10-01 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#inte"
  },
  {
    "path": "architecture/SSTable/Iterator_Wrapper - 2018-10-03 - rsy.md",
    "chars": 558,
    "preview": "# IteratorWrapper - 2018-10-03 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [相关依赖"
  },
  {
    "path": "architecture/SSTable/LSM - 2018-10-06 - rsy.md",
    "chars": 1830,
    "preview": "# LSM - 2018-10-06 rsy\n\n- [模块概要](#module_in_brief)\n- [参考资料](#reference)\n\n\n&nbsp;   \n<a id=\"module_in_brief\"></a>\n## 模块概要"
  },
  {
    "path": "architecture/SSTable/MergingIterator - 2018-10-05 - rsy.md",
    "chars": 1947,
    "preview": "# MergingIterator - 2018-10-05 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明"
  },
  {
    "path": "architecture/SSTable/README.md",
    "chars": 5319,
    "preview": "- [FilterBlock](#filterblock)\n- [Block](#block)\n- [Table](#table)\n- [MergingIterator](#mergingiterator)\n- [TwoLevelItera"
  },
  {
    "path": "architecture/SSTable/Table - 2018-10-04 - rsy.md",
    "chars": 4743,
    "preview": "# Table - 2018-10-02 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interfa"
  },
  {
    "path": "architecture/SSTable/TwoLevelIterator - 2018-10-03 - rsy.md",
    "chars": 1774,
    "preview": "# TwoLevelIterator - 2018-10-03 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说"
  },
  {
    "path": "architecture/util/Arena/arena - 2018-09-30 - rsy.md",
    "chars": 1709,
    "preview": "# arena - 2018-09-27 rsy\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interfa"
  },
  {
    "path": "architecture/util/BloomFilter/BloomFilter - 2018-10-10 mk.md",
    "chars": 1082,
    "preview": "# BloomFilter - 2018-10-10 毛恺\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#in"
  },
  {
    "path": "architecture/util/Coding/coding - 2018-09-06 - rsy.md",
    "chars": 1911,
    "preview": "# coding - 2018-09-26 rsy\n\n- [模块信息](#module_info)\n- [模块功能](#module_function)\n- [接口说明](#interface_specification)\n- [内部实现细"
  },
  {
    "path": "architecture/util/Comparator/Comparator - 2018-10-10 - mk.md",
    "chars": 1003,
    "preview": "# Comparator - 2018-10-10 毛恺\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [相关依赖说明](#d"
  },
  {
    "path": "architecture/util/Env_Options/Env - 2018-10-04 -jyh.md",
    "chars": 1899,
    "preview": "# Env - 2018-10-04 季宇恒\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interface"
  },
  {
    "path": "architecture/util/Env_Options/Options - 2018-10-04 - jyh.md",
    "chars": 1387,
    "preview": "# Options - 2018-10-04 季宇恒\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#inter"
  },
  {
    "path": "architecture/util/Env_Options/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/util/LRU/cache - 2018-09-20 - rsy.md",
    "chars": 6633,
    "preview": "# Cache - 2018-09-20 - rsy\n\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interface_specification)\n- ["
  },
  {
    "path": "architecture/util/Logging/Logging - 2018-10-14 - jyh.md",
    "chars": 1517,
    "preview": "# Logging - 2018-10-14 季宇恒\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#inter"
  },
  {
    "path": "architecture/util/Logging/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/util/README.md",
    "chars": 1635,
    "preview": "# util 整合\n\n\n- [Arena](#Arena)\n- [Coding](#Coding)\n- [ShardedLRUCache](#ShardedLRUCache)\n- []()\n- []()\n- []()\n\n\n\n\n&nbsp; "
  },
  {
    "path": "architecture/util/memenv/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/util/memtable/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/util/memtable/skiplist_2018_10_12_ss.md",
    "chars": 3189,
    "preview": "# Module - 2018-10-12 苏胜\r\n\r\n- [模块信息](#module_info)\r\n- [模块概要](#module_in_brief)\r\n- [模块功能](#module_function)\r\n- [接口说明](#in"
  },
  {
    "path": "architecture/平台相关-锁-信号-原子-压缩/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/平台相关-锁-信号-原子-压缩/atomic/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/平台相关-锁-信号-原子-压缩/posix/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "architecture/平台相关-锁-信号-原子-压缩/snappy/README.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "doc/SA.md",
    "chars": 34321,
    "preview": "# LevelDB\n\n![](assets/leveldb_icon_09_26.png)\n\n## Abstract\nLevelDB is an open source on-disk key-value store written by "
  },
  {
    "path": "doc/Understand配置及使用/README.md",
    "chars": 737,
    "preview": "clone [这个仓库](https://github.com/rsy56640/leveldb) 到本地\n\n打开 understand,点击 File -> New -> Project   \n\n![](assets/new_projec"
  },
  {
    "path": "doc/plan.md",
    "chars": 13997,
    "preview": "# Plan\n\n**推荐使用的源码阅读工具:Source Insight / Understand (Sci-Tool)**  \n\n项目地址:   \n[rsy56640/leveldb - forked](https://github.co"
  },
  {
    "path": "doc/文档格式模板.md",
    "chars": 582,
    "preview": "# Module - 2018-mm-dd xxx\n\n- [模块信息](#module_info)\n- [模块概要](#module_in_brief)\n- [模块功能](#module_function)\n- [接口说明](#interf"
  },
  {
    "path": "doc/文档格式说明.md",
    "chars": 2321,
    "preview": "# 项目文档格式说明\n-----\n\n文中所有非引用部分均为格式要求部分。引用部分为格式要求的解释。(此句除外)  \n\n### 文档样例:[]()\n\n-----\n\n>所有文档均采用md格式,推荐使用编辑器 MarkdownPad2 / Typ"
  },
  {
    "path": "doc/第十四组 - 进度报告.md",
    "chars": 12873,
    "preview": "# 第十四组 - 进度报告\n\n&nbsp;   \n**软件体系架构恢复**:\n\n- [总体架构](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/doc/%E"
  },
  {
    "path": "doc/软件体系结构.md",
    "chars": 322,
    "preview": "# LevelDB\n\n-----\n\n![](assets/leveldb_icon_09_26.png)\n\nLevelDB is a fast key-value storage library written at Google that"
  },
  {
    "path": "reference/README.md",
    "chars": 6464,
    "preview": "# Reference\n\n- [leveldb实现解析 - 淘宝-核心系统研发-存储](https://github.com/rsy56640/read_and_analyse_levelDB/blob/master/reference/D"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the rsy56640/read_and_analyse_levelDB GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 58 files (183.3 KB), approximately 70.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!