分享
分享赚钱 收藏 举报 版权申诉 / 34

类型leveldb实现解析.pdf

  • 上传人:精品资料
  • 文档编号:7885543
  • 上传时间:2019-05-29
  • 格式:PDF
  • 页数:34
  • 大小:661.21KB
  • 配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    leveldb实现解析.pdf
    资源描述:

    1、 leveldb 实现解析 淘宝 -核心系统 研发 -存储 那岩 2011-12-13 目录 一、 代码目录结构 . 1 1. doc/ 1 2. include/leveldb/ . 1 3. db/ . 1 4. table/. 1 5. port/ . 1 6. util/ . 1 7. helper/memenv/ . 1 二、 基本概念 1 1. Slice (include/leveldb/slice.h) 1 2. Option ( include/leveldb/option.h) 2 3. Env ( include/leveldb/env.h util/env_posix

    2、.h) 3 4. varint (util/coding.h) . 3 5. ValueType ( db/dbformat.h) 3 6. SequnceNnumber ( db/dbformat.h) . 4 7. user key. 4 8. ParsedInternalKey ( db/dbformat.h) 4 9. InternalKey (db/dbformat.h) 4 10. LookupKey (db/dbformat.h) 4 11. Comparator ( include/leveldb/comparator.h util/comparator.cc) 4 12. I

    3、nternalKeyComparator (db/dbformat.h) 5 13. WriteBatch ( db/write_batch.cc) . 5 14. Memtable (db/memtable.cc db/skiplist.h) 5 15. Sstable (table/table.cc) . 5 16. FileMetaData ( db/version_edit.h) . 5 17. block (table/block.cc) . 6 18. BlockHandle(table/format.h) 6 19. FileNumber( db/dbformat.h) 6 20

    4、. filename (db/filename.cc) 6 21. level-n (db/version_set.h) . 7 22. Compact (db/db_impl.cc db/version_set.cc) 7 23. Compaction(db/version_set.cc) 7 24. Version (db/version_set.cc) 8 25. VersionSet ( db/version_set.cc) . 9 26. VersionEdit(db/version_edit.cc) . 10 27. VersionSet:Builder (db/version_s

    5、et.cc) . 11 28. Manifest( descriptor)( db/version_set.cc) 12 29. TableBuilder/BlockBuilder(table/table_builder.cc table/block_builder.cc) . 12 30. Iterator (include/leveldb/iterator.h) . 12 三、 存储结构的格式定义与操作 12 1. memtable (db/skiplist.h db/memtable) 12 2. block of sstable (table/block_builder.cc tabl

    6、e/block.cc) . 14 3. sstable (table/table_bulder.cc/table.cc) 16 4. block of log ( db/log_format.h db/log_writer.cc db/log_reader.cc) 18 5. log ( db/log_format.h db/log_writer.cc db/log_reader.cc) 18 6. cache( util/cache.cc) 19 7. Snapshot ( include/leveldb/snapshot.h) . 19 8. Iterator (include/level

    7、db/iterator.h) . 19 四、 主要流程 . 24 1. open . 24 2. put 25 3. get 25 4. delete 26 5. snapshot 26 6. NewIterator . 26 7. compact. 26 五、 总结 . 30 1. 设计 /实现中的优化 . 30 2. 可以做的优化 31 1 leveldb 是 Google 开源的持久化 KV 单机存储引擎,开源页面 http:/ 针对存储面对的普遍随机 IO 问题, leveldb 采用了 merge-dump 的方式,将逻辑场景的写请求转换成 顺序写log 和写 memtable 操作

    8、, 由后台进程将 memtable 持久化成 sstable。对于读请求,随机 IO 还是无法避免,但它设计了一系列策略来保证读的效率 。 这里对 leveldb 的实现做具体解析 , 但 并不采用对代码 注释的方式, 而是意图从上层设计的角度, 将内部的实现逻辑串联起来, 尽量 发现 策略 设计 背后的原因。 一、 代码 目录 结构 1. doc/ 相关文档 。有 log 和 sstable 的格式介绍 ( log_format/table_format) 。 2. include/leveldb/ 使用者需要的头文件,包含基本的接口 ,可以自定义的 comparator/env/cache

    9、,以及依赖的头文件。 3. db/ 主要逻辑的实现。包括接口的实现( db_impl/db_iter) ,内部 结构的定义( dbformat/memtable/skiplist/write_batch) , db 运行状态 以及操作 的包装( version_set/version_edit) , log 格式相关( log/log_reader/log_writer) ,filename 处理相关( filename), sstable 相关( builder/table_cache) . 4. table/ sstable 相关的数据格式定义以及操作实现。 格式定义( format),

    10、block 相关的操作( block/block_builder) ,sstable 相关的操作( table/table_builder) ,操作便利封装的复合 Iterator(two_level_iterator/ merger),优化Iterator 的 wrapper( iterator_wrapper)。 5. port/ 根据系统环境, 为移植实现的锁 /信号 /原子操作 /压缩 相关 。 提供 posix/android。 6. util/ 提供的通用功能 实现 。 memtable 使用的简单内存管理 ( arena) ,LRU cache 的实现( cache) , com

    11、parator 的默认实现( comparator),通用功能的实现( coding/crc32c/hash/random/MutexLock/logging) ,leveldb将文件 /进程相关的操作封装成 Env,提供了默认的实现( env_posix) 。 7. helper/memenv/ 实现了一个 简单的完全内存的 文件系统,提供操作目录文件的接口。 二、 基本概念 1. Slice (include/leveldb/slice.h) 为 操作数据的方便,将数据和长度包装成 Slice 使用,直接操控指针避免不必要的数据拷贝。 2 class Slice private: cons

    12、t char* data_; size_t size_; ; 2. Option ( include/leveldb/option.h) leveldb 中启动时 的一些配置,通过 Option 传入, get/put/delete 时,也有相应 的ReadOption/WriteOption。 / include/leveldb/option.h Options / 传入的 comparator const Comparator* comparator; / open 时,如果 db 目录不存在就创建 bool create_if_missing; / open 时,如果 db 目录存在就报

    13、错 bool error_if_exists; / 是否保存中间的错误状态 (RecoverLog/compact),compact 时是否读到的 block 做检验。 bool paranoid_checks; / 传入的 Env。 Env* env; / 传入的打印日志的 Logger Logger* info_log; / memtable 的最大 size size_t write_buffer_size; / db 中打开的文件最大个数 / db 中需要打开的文件包括基本的 CURRENT/LOG/MANIFEST/LOCK, 以及打开的 sstable 文件。 / sstable

    14、一旦打开,就会将 index 信息加入 TableCache,所以把 / (max_open_files - 10)作为 table cache 的最大数量 . int max_open_files; / 传入的 block 数据的 cache 管理 Cache* block_cache; / sstable 中 block 的 size size_t block_size; / block 中对 key 做前缀压缩的区间长度 int block_restart_interval; / 压缩数据使用的压缩类型(默认支持 snappy,其他类型需要使用者实现) CompressionType c

    15、ompression; / include/leveldb/option.h struct ReadOptions / 是否对读到的 block 做校验 bool verify_checksums; / 读到的 block 是否加入 block cache bool fill_cache; / 指定读取的 SnapShot 3 const Snapshot* snapshot; / include/leveldb/option.h struct WriteOptions / write 时,记 binlog 之后,是否对 binlog 做 sync。 bool sync; / 如果传入不为 N

    16、ULL, write 完成之后同时做 SnapShot. const Snapshot* post_write_snapshot; 另外还有一些编译时的常量,与 Option 一起控制。 / db/dbformat.h namespace config / level 的最大值 static const int kNumLevels = 7; / level-0 中 sstable 的数量超过这个阈值,触发 compact static const int kL0_CompactionTrigger = 4; / level-0 中 sstable 的数量超过这个阈值 , 慢处理此次写 (sl

    17、eep1ms) static const int kL0_SlowdownWritesTrigger = 8; / level-0 中 sstable 的数量超过这个阈值 , 阻塞至 compact memtable 完成。 static const int kL0_StopWritesTrigger = 12; / memtable dump 成的 sstable,允许推向的最高 level / (参见 Compact 流程的 VersionSet:PickLevelForMemTableOutput()) static const int kMaxMemCompactLevel = 2;

    18、/ db/version_set.cc namespace leveldb / compact 过程中, level-0 中的 sstable 由 memtable 直接 dump 生成,不做大小限制 / 非 level-0 中的 sstable 的大小设定为 kTargetFileSize static const int kTargetFileSize = 2 * 1048576; / compact level-n 时,与 level-n+2 产生 overlap 的数据 size (参见 Compaction) static const int64_t kMaxGrandParentO

    19、verlapBytes = 10 * kTargetFileSize; 3. Env ( include/leveldb/env.h util/env_posix.h) 考虑到移植以及灵活性, leveldb 将系统相关的处理(文件 /进程 /时间之类 ) 抽象成 Env,用户可以自己实现相应的接口,作为 Option 传入。默认使用自带的 实现 。 4. varint (util/coding.h) leveldb 采用了 protocalbuffer 里使用的变长整形编码方法,节省空间 。 5. ValueType ( db/dbformat.h) leveldb 更新( put/dele

    20、te)某个 key 时 不会操控到 db 中的数据,每次操作都是直接 新 插入一份 kv 数据 ,具体的数据合并和清除由后台的 compact 完成。所以,每次 put, db 中就会新加入一份 KV 数据,即使该 key 已经存在;而 delete 等同于 put 空的 value。为了区分真实 kv 数据和删除操作的 mock 数据,使用 ValueType 来标识: 4 enum ValueType kTypeDeletion = 0x0, kTypeValue = 0x1 ; 6. SequnceNnumber ( db/dbformat.h) leveldb 中的每次更新( put/

    21、delete)操作都拥有一个版本,由 SequnceNumber 来标识,整个 db 有一个全局值保存着当前使用到的 SequnceNumber。 SequnceNumber 在 leveldb 有重要的地位, key 的排序,compact 以及 snapshot 都依赖于它。 typedef uint64_t SequenceNumber; 存储时, SequnceNumber 只占用 56 bits, ValueType 占用 8 bits,二者共同占用 64bits( uint64_t). 0 56 SequnceNumber ValueType 7. user key 用户层面传入的

    22、 key,使用 Slice 格式。 8. ParsedInternalKey ( db/dbformat.h) db 内部操作的 key。 db 内部需要将 user key 加入元信息 (ValueType/SequenceNumber)一并做处理。 struct ParsedInternalKey Slice user_key; SequenceNumber sequence; ValueType type; ; 9. InternalKey (db/dbformat.h) db 内部, 包装易用的结构,包含 userkey 与 SequnceNumber/ValueType。 10. L

    23、ookupKey (db/dbformat.h) db 内部在为查找 memtable/sstable 方便,包装使用的 key 结构,保存有 userkey 与SequnceNumber/ValueType dump 在内存的数据。 class LookupKey private: const char* start_; const char* kstart_; const char* end_; ; LookupKey: start kstart end userkey_len (varint32) userkey_data (userkey_len) SequnceNumber/Valu

    24、eType (uint64) 对 memtable 进行 lookup 时使用 start,end, 对 sstable lookup 时使用 kstart, end。 11. Comparator ( include/leveldb/comparator.h util/comparator.cc) 对 key 排序时使用的比较方法。 leveldb 中 key 为升序。 5 用户可以自定义 user key 的 comparator( user-comparator),作为 option 传入,默认采用 byte compare( memcmp)。 comparator 中有 FindSho

    25、rtestSeparator() / FindShortSuccessor()两个接口,FindShortestSeparator( start, limit)是 获得大于 start 但小于 limit 的最小值。 FindShortSuccessor( start)是获得比 start 大的最小值。比较都基于 user-commparator,二者会 被用来确定 sstable 中 block 的 end-key。 12. InternalKeyComparator (db/dbformat.h) db 内部做 key 排序时使用的比较方法。排序时,会先使用 user-comparator

    26、 比较 user-key,如果user-key 相同,则比较 SequnceNumber, SequnceNumber 大的为小。因为 SequnceNumber 在 db 中全局递增,所以,对于 相同的 user-key,最新的更新( SequnceNumber 更大)排在前面,在查找的时候,会 被 先 找到。 InternalKeyComparator 中 FindShortestSeparator() / FindShortSuccessor()的实现,仅 从 传入的内部 key 参数,解析出 user-key, 然后再 调用 user-comparator 的对应接口 。 13. Wr

    27、iteBatch ( db/write_batch.cc) 对 若干数目 key 的 write 操作( put/delete) 封装成 WriteBatch。 它会将 userkey 连同SequnceNumber 和 ValueType 先做 encode,然后做 decode,将数据 insert 到指定的 Handler( memtable)上面。 上层的处理逻辑简洁,但 encode/decode 略有冗余。 WriteBatch encode 之后, 内部 保存 的 数据格式 : SequnceNumber (uint64) count (uint32) record0 recor

    28、dN record 组成: ValueType( char) key_len (varint32) key_data (key_len) value_len (varint32) value_data (value_len) 1) SequnceNumber: WriteBatch 中开始使用的 SequnceNumber。 2) count: 批量处理的 record 数量 3) record:封装在 WriteBatch 内的数据。 如果 ValueType 是 kTypeValue,则后面有 key 和 value 如果 ValueType 是 kTypeDeletion,则后面只有 k

    29、ey。 14. Memtable (db/memtable.cc db/skiplist.h) db 数据在内存中的存储格式。写操作的数据都会先写到 memtable 中。 memtable 的 size 有限制最大值( write_buffer_size)。 memtable 的 实现 是 skiplist。 当一个 memtable size 到达阈值时,会变成只读的 memtable( immutable memtable),同时生成一个新的 memtable 供新的写入。后台的 compact 进程会负责将 immutable memtable dump 成 sstable。 所以,

    30、同时最多会存在两个 memtable(正在写的 memtable 和 immutable memtable) 。 15. Sstable (table/table.cc) db 数据持久化的文件。文件的 size 有限制最大值( target_file_size)。文件前面为数据,后面是索引元信息。 16. FileMetaData ( db/version_edit.h) sstable 文件的 元信息 封装成 FileMetaData, struct FileMetaData int refs; / 引用计数 6 int allowed_seeks; / compact 之前允许的 see

    31、k 次数( 参见 Version) uint64_t number; / FileNumer uint64_t file_size; / 文件的 size InternalKey smallest; / sstable 文件的最小 key InternalKey largest; / sstable 文件的最大 key ; 17. block (table/block.cc) sstable 的数据由一个个的 block 组成。当持久化数据时,多份 KV 聚合成 block 一次写入;当读取时,也是以 block 单位做 IO。 sstable 的索引信息中会保存 符合 key-range 的

    32、 block 在文件中的offset/size( BlockHandle) 。 18. BlockHandle(table/format.h) block 的元信息(位于 sstable 的 offset/size) 封装成 BlockHandle。 19. FileNumber( db/dbformat.h) db 创建文件 时会按照规则 将 FileNumber 加上 特定后缀 作为文件名 。 所以, 运行 时 只需要 记录FileNumber( uint64_t)即可 定位到具体的文件路径 ,省掉 了 字符串的麻烦。 FileNumber 在 db 中全局递增。 20. filename

    33、 (db/filename.cc) db 中的文件用文件名区分类型。 有以下几种 类型 enum FileType kLogFile, kDBLockFile, kTableFile, kDescriptorFile, kCurrentFile, kTempFile, kInfoLogFile / Either the current one, or an old one ; 1) kLogFile 日志文件: 0-9+.log leveldb 的写流程是先记 binlog,然后写 sstable,该日志文件即是 binlog。前缀数字为FileNumber。 2) kDBLockFile,

    34、lock 文件: LOCK 一个 db 同时只能有一个 db 实例操作,通过对 LOCK 文件加文件锁 ( flock) 实现主动保护。 3) kTableFile, sstable 文件: 0-9+.sst 保存数据的 sstable 文件。前缀为 FileNumber。 4) kDescriptorFile, db 元信息文件: MANIFEST-0-9+ 每当 db 中的状态改变( VersionSet),会将这次改变( VersionEdit) 追加到 descriptor 文件中。 后缀数字为 FileNumber。 5) kCurrentFile,: CURRENT CURRENT

    35、 文件中保存当前 使用的 descriptor 文件的文件名。 6) kTempFile,临时文件: 0-9+.dbtmp 对 db 做修复( Repairer)时,会产生临时文件。 前缀为 FileNumber。 7) kInfoLogFile, db 运行时打印日志的文件: LOG db 运行时,打印的 info 日志保存在 LOG 中。 每次重新运行,如果已经存在 LOG 文件,会先将 LOG文件重名成 LOG.old 7 21. level-n (db/version_set.h) 为了均衡读写的效率, sstable 文件分层次( level)管理, db 预定义了最大的 level

    36、 值。 compact 进程负责 level 之间的均衡。 22. Compact (db/db_impl.cc db/version_set.cc) db 中有一个 compact 后台进程,负责将 memtable 持久化成 sstable,以及均衡整个 db 中各 level 的sstable。 Comapct 进程会优先将已经写满的 memtable dump 成 level-0 的 sstable(不会合并相同key 或者清理已经删除的 key)。然后,根据设计的策略选取 level-n 以及 level-n+1 中有 key-range overlap 的 几个 sstable 进

    37、行 merge(期间会合并相同的 key 以及清理删除的 key),最后生成若干个level-(n+1)的 ssatble。随着数据不断的写入和 compact 的进行,低 level 的 sstable 不断向高level 迁移。 level-0 中的 sstable 因为是由 memtable 直接 dump 得到,所以 key-range 可能 overlap,而 level-1 以及更高 level 中的 sstable 都是做 merge 产生,保证了位于同 level 的 sstable 之间,key-range 不会 overlap, 这个特性有利 于读的处理。 23. Comp

    38、action(db/version_set.cc) compact 信息的 封装。 class Compaction int level_; / 要 compact 的 level uint64_t max_output_file_size_; / 生成 sstable 的 最大 size (targe_file_size) Version* input_version_; / compact 时当前的 Version VersionEdit edit_; / 记录 compact 过程中的 操作 / inputs_0为 level-n 的 sstable 文件信息, / inputs_1为

    39、level-n+1 的 sstable 文件信息 std:vector inputs_2; / 位于 level-n+2,并且与 compact 的 key-range 有 overlap 的 sstable。 / 保存 grandparents_是因为 compact 最终会生成一系列 level-n+1 的 sstable, / 而如果生成的 sstable 与 level-n+2 中有过多的 overlap 的话,当 compact / level-n+1 时,会产生过多的 merge,为了尽量避免这 种情况, compact 过程中 / 需要检查与 level-n+2 中产生 over

    40、lap 的 size 并与 / 阈值 kMaxGrandParentOverlapBytes 做比较, / 以便提前中止 compact。 std:vector grandparents_; / 记录 compact 时 grandparents_中已经 overlap 的 index size_t grandparent_index_; / 记录是否已经有 key 检查 overlap / 如果是第一次检查,发现有 overlap,也不会增加 overlapped_bytes_. / (没有看到这样做的意义) bool seen_key_; / 记录 已经 overlap 的 累计 size

    41、 int64_t overlapped_bytes_; / compact 时,当 key 的 ValueType 是 kTypeDeletion 时, / 要检查其在 level-n+1 以上是否存在( IsBaseLevelForKey()) / 来决定是否丢弃掉该 key。因为 compact 时, key 的遍历是顺序的, / 所以每次检查 从上一次检查结束的地方开始即可, / level_ptrs_i中 就 记录了 input_version_-levels_i中 , 上一次 比较 结束 的 8 / sstable 的 容器 下标 。 size_t level_ptrs_config

    42、:kNumLevels; ; 24. Version (db/version_set.cc) 将每次 compact 后的最新数 据状态定义为 Version,也就是 当前 db 元信息以及 每个 level 上具有 最新数据状态的 sstable 集合。 compact 会在某个 level 上新加入或者删除一些 sstable,但可能这个时候,那些要 删除的 sstable 正在被读,为了处理这样的读写竞争情况,基于 sstable文件一旦生成就不会改动的特点,每个 Version 加入引用计数,读以及解除读操作会将引用计数相应加减一。 这样, db 中可能 有多个 Version 同时存

    43、在 (提供服务) ,它们通过链表链接起来。当 Version 的引用计数为 0 并且不是当前最新的 Version 时,它会从链表中移除, 对应的, 该 Version 内的 sstable 就可以 删除 了(这些废弃的 sstable 会在下一次 compact 完成时被清理掉)。 class Version / 属于的 VersionSet VersionSet* vset_; / 链表指针 Version* next_; Version* prev_; / 引用计数 int refs_; / 每个 level 的 所有 sstable 元 信息 。 / files_i中的 FileMet

    44、aData 按照 FileMetaData:smallest 排序 , / 这是在每次更新都保证的。(参见 VersionSet:Builder:Save()) std:vector files_config:kNumLevels; / 需要 compact 的文件( allowed_seeks 用光 ) FileMetaData* file_to_compact_; / file_to_compact_的 level int file_to_compact_level_; / 当前最大的 compact 权重以及对应的 level double compaction_score_; int

    45、compaction_level_; ; Version 中与 compact 相关的有 file_to_compact_/ file_to_compact_level_, compaction_score_/ compaction_level_,这里详细说明他们的意义。 1) compaction_score_ leveldb 中分 level 管理 sstable,对于写,可以认为与 sstable 无关。而基于 get 的流程 (参见get 流程) ,各 level 中的 sstable 的 count, size 以及 range 分布,会直接影响读的效率。可以预想的最佳情形可能是 l

    46、evel-0 中最多有一个 sstable, level-1 以及之上的各 level 中 key-range 分布均匀,期望更多的查找可以遍历最少的 level 即可定位到。 将 这种预 想的最佳状 态 定 义 成 : level 处于均衡的状态。当采用具体的参数量化,也就量化了各个level 的不均衡比重,即 compact 权重: score。 score 越大,表示该 level 越不均衡,需要更优先进行 compact。 每 个 level 的具体均衡参数及比重计算策略如下: a. 因 为 level-0 的 sstable range 可能 overlap,所以如果 level-0 上有过多的 sstable,在做查找时,会严重影响效率。同时,因为 level-0 中的 sstable 由 memtable 直接 dump 得到,并不受9 kTargetFileSize(生成 sstable 的 size)的控制,所以 sstable 的 count 更有意义。基于此,对于 level-0, 均衡的状 态 需要 满 足: sstable 的 count DeletedFileSet; / db 一旦创建,排序的逻辑就必须保持兼容,用 compa

    展开阅读全文
    提示  道客多多所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
    关于本文
    本文标题:leveldb实现解析.pdf
    链接地址:https://www.docduoduo.com/p-7885543.html
    关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

    道客多多用户QQ群:832276834  微博官方号:道客多多官方   知乎号:道客多多

    Copyright© 2025 道客多多 docduoduo.com 网站版权所有世界地图

    经营许可证编号:粤ICP备2021046453号    营业执照商标

    1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png



    收起
    展开