LevelDB Log模块
概览
log 相关的功能在如下文件中
- db/log_format.h,这是里格式的定义
- db/log_reader.h,log_reader.cc
- db/log_writer.h,log_writer.cc
一个日志块大小固定为 32K
每个日志记录格式如下(4字节的checksum、2字节长度、1字节类型)
类型包括:
- kZeroType,预留
- kFullType,单个块可以存放下
- kFirstType,跨多个块,这是第一个
- kMiddleType,跨多个块,中间的
- kLastType,跨多个块,最后一个
如下是 3条 记录,其中 A、C是 full类型,B跨了多个块
每个文件块的组成格式,用go
描述如下:
|
|
读写入过程
写入
核心逻辑位于 db/log_writer.cc 中
|
|
例子
- 假设当前已经写入了 1000字节,再写一条 500字节的日志,需要额外增加7个字节(4个字节checksum、2字节长、1字节类型)
- 写入之后,文件块的写入偏移量就变成
1507
字节 - 加入写入新记录为 31755字节,写入后偏移量为 32762字节(1000字节开始偏移量 + 31755新内容长度 + 7字节头部)
- 此时只剩 6字节空余,对于小于 7字节的空余情况,需要全部填充 ‘\0’
另一个例子
写入流程
- AddRecord,在 do-while中,先计算出剩余的字节量,如果少于 7字节则全部填充 0
- 之后计算出数据部分(去掉 7字节头部后),需要的空间,如果小于剩余空间,则为 FULL类型,否则是FIRST类型
- 调用EmitPhysicalRecord,将数据写入磁盘
- EmitPhysicalRecord 中,先计算出头部7字节,填充4字节的CRC部分(这里用的是变长编码)
- 然后向磁盘写入 7字节头部、日志数据部分、最后flush,最后计算新的 offset
- 继续之前的 AddRecord,也就是do-while部分,此时计算剩余的 部分是否 < 块的可用长度,是为LAST类型,否则为MIDDLE类型
- 继续不停的写,直到数据写完退出循环
读取
读取相关的逻辑位于
- db/log_reader.h
- db/log_reader.cc
核心逻辑是:
|
|
主要逻辑
- 调用 ReadPhysicalRecord,从物理文件中读取数据块
- ReadPhysicalRecord 中会从物理文件中读取 32K数据,再解析头部(长度和类型,以及CRC)
- 如果解析成功则将数据封装成 Slice指针(此变量作为函数参数传进来的),并返回 type
- 如果是 FULL 类型,将结果赋予 record指针返回
- FIRST、MIDDLE 则放入 scratch(临时的std::string缓存)中,LAST则封装成Slice返回
- 这里还会处理 读取失败,块损坏等情况,并记录到 Reporter 对象中
写日志的触发
这是在 db/db_impl.cc 中
|
|
Log文件记录的内容如下:
- 序列号(sequenceNumber):占8个字节,表示该批次的序列号
- 个数(count):占4个字节,表示该批次有多少键-值对
- 类型(type):0x0表示删除该批次键-值对,0x1表示新增键-值对
恢复
当 log 对应的 mem-table已经写入磁盘后,log就可以删除了
如果启动时发现有 log 则需要做对应的恢复处理
恢复的实现在: db/db_impl.cc 中
函数为:
|
|
触发过程是:
|
|
流程:
- 创建 log::Reader,然后调用 ReadRecord()读取一个日志块
- 创建 MemTable(如果为null的话),并将刚刚读取的日志记录,批量写入到 MemTable 中
- 生成SequenceNumber,并更新 max_sequence
- 如果 MemTable 大小超过阈值,则写入到 LevelDB 0 中
- 之后检查是否要重用上面的 log, options_.reuse_logs
- WriteLevel0Table,获取迭代器 mem->NewIterator(),然后将其写入
- 写完之后,还会更新 VersionEdit 文件
参考
- github index
- Bloom Filters by Example
- 漫谈 LevelDB 数据结构(一):跳表(Skip List)
- 漫谈 LevelDB 数据结构(二):布隆过滤器(Bloom Filter)
- 漫谈 LevelDB 数据结构(三):LRU 缓存( LRUCache)
- LevelDB源码剖析
- SF-Zhou’s Blog
- leveldb-handbook
- 庖丁解LevelDB
- rust使用
- LevelDB使用介绍
- LeveLDB维基百科
- dbdb.io的LevelDB介绍
- MariaDB的插件
- 书籍:精通LevelDB
- leveldb 实现解析
- POSIX™ 1003.1 Frequently Asked Questions (FAQ Version 1.18)
- Spurious wakeup
- Memory barrier
- Memory Barriers: a Hardware View for Software Hackers
- Bean Li的LevelDB文章汇总