leveldb主要操作流程
Open
- open主要有两个功能:加载元数据重建current version和对log进行重做
graph TD;
DB::Open==>DBImpl::Recover
DBImpl::Recover-->Env::LockFile
Env::LockFile-->Env::FileExists{{判断是否存在CURRENT文件}}
Env::FileExists-->|不存在|DBImpl::NewDB[创建第一个VersionEdit并保存到Manifest文件, 创建CURRENT文件]
Env::FileExists---->|存在|VersionSet::Recover
VersionSet::Recover-->Reader::ReadRecord[从manifest文件中读取每一个VersionEdit]
Reader::ReadRecord-->Builder::Apply[将edit合并到当前状态并对RW和Compact进行评估]
Builder::Apply-->Builder::SaveTo[重建每一层的状态]
Builder::SaveTo-->VersionSet::Finalize[计算出最需compact的level]
VersionSet::Finalize-->D[保存last_sequence, log_num, next_file_num等元数据]
D-->BImpl::RecoverLogFile[重做log]
DBImpl::Recover==>A[生成log和memtable]
A==>B[如果在recover中生成了新的VersionEdit, 保存到Manifest]
B==>C[删除无用的文件并进行comapct]
Write
- write先进行批量写日志操作,这是保证数据不丢失的关键。但是如果保障数据真正不丢失,必须设置sync=true,这对性能有很大的影响
- 只有两个memtable,是否出于快速恢复的考虑?增加memtable的大小或者个数是否对性能有提升?
- level-0的文件数据有限制,主要是出于Get性能的考虑,level-0文件区间相加会导致查询的时候遍历整层的文件
- 同步机制,只有一个线程能够写操作,在使用机械硬盘的情况下,cpu不是限制(redis也是单线程的)。但是在高性能存储硬件的情况下,cpu是否会成为限制
graph TD;
DBImpl::Write==>A[只有writer队列的第一个线程能够执行write操作, 其他的线程都需要等待]
A==>DBImpl::MakeRoomForWrite
DBImpl::MakeRoomForWrite-->B[如果mem未达到限制直接返回]
B-->C[如果mem达到限制, 等待imem持久化]
C-->D[如果mem达到限制, 等待level-0文件的数目达到限制范围内]
D-->E[创建新的log和mem, 进行compact]
DBImpl::MakeRoomForWrite==>DBImpl::BuildBatchGroup[合并写操作]
DBImpl::BuildBatchGroup==>Writer::AddRecord[保存操作log]
Writer::AddRecord==>WriteBatchInternal::InsertInto[将数据保存到mem]
WriteBatchInternal::InsertInto==>F[通知批量写操作中完成的线程, 通知队列中的第一个线程]
Get
- Get操作需要使用sequence来构造inner_key,从新到旧查找第一个大于inner_key的key
- imem和mem,就是SkipList的查找过程
- 在sstable文件中查找时,缓存会很大程度上影响Get的性能
graph TD;
DBImpl::Get==>A[判断是否设置快照, 获取构造inner_key的sequence]
A==>B[增加mem, imem, currentVerion的引用, 防止被释放]
B==>MemTable::Get[先后从mem, imem, sstable中进行查询]
MemTable::Get-->C[获取第一个大于等于inner_key的节点, 判断user_key是否相等, inner_key中sequence是逆序排列的, 先获取最新的key]
MemTable::Get====>Version::Get[查询过程中会记录状态, 便于进行compact]
Version::Get-->Version::ForEachOverlapping
Version::ForEachOverlapping-->D[level-0的文件可能相交, 需要从新到旧遍历所有文件, 具体查找实现在Table]
D-->E[level-n层不相交, 二分查找key所在的文件, 同样通过Table进查找]
Version::Get==>F[更新状态, 如果一个文件多次查询不到数据, 会进行compact, 减小对Get的影响]
F-->G[减少mem, imem, currentVerion的引用]
Compact
compact操作时最复杂的,主要分为以下几个部分
- CompactMemTable:将memtable中的数据持久化到sstable,优先级最高,并且minor comapct时也要进行
- 选择compact的文件或者level, 新生成sstable文件时统计level_to_compact,Get时统计file_to_compact
- level+1层进行compact的文件区间范围必须覆盖level层进行compact的文件区间范围
- 如果level层某个文件的区间和level+1层很多个文件相交会导致两个结果:1. 该文件会有很多无效查询 2. 该文件的compact会占用大量io
- MergingIterator的时间复杂为O(2*n)
graph TD;
class A cssClass
DBImpl::MaybeScheduleCompaction==>A[compact操作运行在后端同一个线程, 同时只能有一个comapct任务进行]
A==>CompactMemTable[完成后imem的comapct后, 便会立即退出]
CompactMemTable------>DBImpl::WriteLevel0Table[创建新的sstable文件, 为imem创建迭代器]
DBImpl::WriteLevel0Table-->BuildTable
BuildTable-->B[遍历迭代器将imemtable中的数据放入TableBuilder, TableBuilder完成持久化]
B-->Version::PickLevelForMemTableOutput[选择该sstable所在的level, 未防止多次compact, 该sstable在某些情况下可以直接放到level+2层]
Version::PickLevelForMemTableOutput-->C[将sstable文件的元数据信息放入Edit]
C-->VersionSet::LogAndApply[将Edit信息放入Manifest文件, 生成新版本]
VersionSet::LogAndApply-->D[删除不需要的文件, 主要是imem对应的log文件]
CompactMemTable==>VersionSet::PickCompaction[获取需要进行compact的文件]
VersionSet::PickCompaction-->E[current_记录了最需要进行comapct的level]
E-->F[每一层记录了上次Compact的最大key, 这次从key开始comapct]
F-->G[如果没有最需进行compact的level, 那么选择最需进行comapct的文件]
G-->H[如果是level-0进行compact, 会获取指定范围内所有相交的文件]
H-->VersionSet::SetupOtherInputs
VersionSet::SetupOtherInputs-->VersionSet::AddBoundaryInputs[添加level层相加的文件]
VersionSet::AddBoundaryInputs-->Version::GetOverlappingInputs[获取level层的区间, 然后获取在level+1层相交的文件]
Version::GetOverlappingInputs-->I[获取两层的总区间, 再次从level获取相交文件, 并且获取文件的返回不能超过总区间, 从level层获取尽量多的文件]
I-->J[从level+2层获取文件]
J-->K[记录此次compact的范围, 下次comapct从这里进行]
VersionSet::PickCompaction==>L[开始进行compact]
graph TD;
L[开始进行compact]==>M[如果level层只有一个文件并且level+1层没有文件, 和level+2相交的文件的数目不超过10, 直接将该文件移动到level+1层]
L==>DBImpl::DoCompactionWork
DBImpl::DoCompactionWork-->N[记录最小快照, 该快照之后的数据不能丢弃]
N-->VersionSet::MakeInputIterator[创建MergingIterator, 除了level0, 每个level创建一个Iterator]
VersionSet::MakeInputIterator==>O[遍历MergingIterator创建TableBuilder进行持久化]
O-->P[minor compact过程中需要对imm_进行comapct, 防止长时间无法生成mem]
P-->Q[如果comapct过程中两个key区间和level+2的10个文件相交, 停止向同一个sstable文件继续加入key]
Q-->R[因为sequence是逆序排列, 所有大于快照的key都不能丢弃, 只保留第一个小于快照的key]
R-->S[sstable文件达到限制大小, 生成新的sstable文件]
S-->T[统计compact状态, 将新生成的sstable的元数据信息保存到manifest]
DBImpl::DoCompactionWork==>U[删除无用的文件, 就是已经compact的文件]

浙公网安备 33010602011771号