LevelDB源码之四LOG文件
“LOG文件在LevelDb中的主要作用是系统故障恢复时,能够保证不会丢失数据。因为在将记录写入内存的Memtable之前,会先写入Log文件,这样即使系统发生故障,Memtable中的数据没有来得及Dump到磁盘的SSTable文件,LevelDB也可以根据log文件恢复内存的Memtable数据结构内容,不会造成系统丢失数据,在这点上LevelDb和Bigtable是一致的。” (http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html)
准备工作:
Log文件只涉及顺序读、顺序写两种操作,而在LevelDB中的其他文件(如SSTable)还会设计随机读操作,作者针对每种操作类型做了封装:
class SequentialFile {
public:
virtual Status Read(size_t n, Slice* result, char* scratch) = ;
virtual Status Skip(uint64_t n) = ;
};
class RandomAccessFile {
public:
virtual Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const = ;
};
class WritableFile {
public:
virtual Status Append(const Slice& data) = ;
virtual Status Close() = ;
virtual Status Flush() = ;
virtual Status Sync() = ;
};
操作系统相关操作在不同系统下,实现各不相同,作者也做了统一的接口封装(跨平台),称之为“环境类”Env:
class Env {
public:
Env() { }
virtual ~Env();
// Return a default environment suitable for the current operating
// system.
static Env* Default();
//基于不同的操作系统实现了各种文件类型的具体类,通过该工厂方法返回。
virtual Status NewSequentialFile(const std::string& fname, SequentialFile** result) = ;
virtual Status NewRandomAccessFile(const std::string& fname, RandomAccessFile** result) = ;
virtual Status NewWritableFile(const std::string& fname, WritableFile** result) = ;
......
};
写入
Leveldb命名空间下,有一个名为log的子命名空间,其下有Writer、Reader两个实现类。按前几节的命名规则,Writer其实是一个Builder,它对外提供了唯一的AddRecord方法用于追加操作记录。
Status Writer::AddRecord(const Slice& slice) {
const char* ptr = slice.data();
size_t left = slice.size();
// Fragment the record if necessary and emit it. Note that if slice
// is empty, we still want to iterate once to emit a single
// zero-length record
Status s;
bool begin = true;
do {
const int leftover = kBlockSize - block_offset_; //1. 当前块剩余大小
assert(leftover >= );
if (leftover < kHeaderSize) //2. 剩余大小不足,占位
{
// Switch to a new block
if (leftover > )
{
// Fill the trailer (literal below relies on kHeaderSize being 7)
assert(kHeaderSize == );
dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));
}
block_offset_ = ;
}
// Invariant: we never leave < kHeaderSize bytes in a block.
assert(kBlockSize - block_offset_ - kHeaderSize >= );
const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
const size_t fragment_length = (left < avail) ? left : avail; //3. 当前块存储的空间大小
RecordType type; //4. Record Type
const bool end = (left == fragment_length);
if (begin && end) {
type = kFullType;
}
else if (begin) {
type = kFirstType;
}
else if (end) {
type = kLastType;
}
else {
type = kMiddleType;
}
s = EmitPhysicalRecord(type, ptr, fragment_length); //5. 写入文件
ptr += fragment_length;
left -= fragment_length;
begin = false;
} while (s.ok() && left > );
return s;
}
备忘如下:
- Log文件被逻辑上分为多个Block,每个Block大小为32K。
- 每条记录由Record Header + Record Content组成,其中Header大小为kHeaderSize(7字节)。
- 当前Block剩余大小不足以填充Record Header时,以"\x00\x00\x00\x00\x00\x00"占位。
- 当Block无法完整记录一条Record时,通过type信息标识该record在当前block中的区块信息,以便读取时可根据type拼接出完整的record。
- EmitPhysicalRecord向Block中插入Record数据
- 每个Record结构如下:
|
Header |
Record Content |
||
|
CRC |
Record Size |
Type |
Record Content |
读取
Log读取逻辑并无特别之处,略。
注:LOG文件和活动的MemTable对应,当活动的MemTable将被切换为非活动(immutable)MemTable时,将创建新的LOG文件。
原本Current、Manifest与Log打算一起备注,但要搞清楚Manifest,LevelDB的版本机制必定要搞清楚,而这本身又是很丰富的内容。
LevelDB源码之四LOG文件的更多相关文章
- LevelDB源码之五Current文件\Manifest文件\版本信息
版本信息有什么用?先来简要说明三个类的具体用途: Version:代表了某一时刻的数据库版本信息,版本信息的主要内容是当前各个Level的SSTable数据文件列表. VersionSet:维护了一份 ...
- LevelDB源码剖析
LevelDB的公共部件并不复杂,但为了更好的理解其各个核心模块的实现,此处挑几个关键的部件先行备忘. Arena(内存领地) Arena类用于内存管理,其存在的价值在于: 提高程序性能,减少Heap ...
- leveldb源码分析--日志
我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...
- leveldb源码分析--Key结构
[注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...
- leveldb 源码--总体架构分析
一 本文目的 对leveldb的总体设计框架分析(关于leveldb基本原理,此文不做阐述,读者可以自行检索文章阅读即可),对leveldb中底层数据存储数据格式,内存数据模型,compact,版本管 ...
- Leveldb源码分析--1
coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- leveldb源码分析--SSTable之block
在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...
- leveldb源码学习系列
楼主从2014年7月份开始学习<>,由于书籍比较抽象,为了加深思考,同时开始了Google leveldb的源码学习,主要是想学习leveldb的设计思想和Google的C++编程规范.目 ...
随机推荐
- nyoj 105 九的余数
点击打开链接 九的余数 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 现在给你一个自然数n,它的位数小于等于一百万,现在你要做的就是求出这个数整除九之后的余数. 输入 ...
- 在SoCEDS环境下编译和更新preloader和uboot程序的方法
在SoCEDS环境下编译和更新preloader和uboot程序的方法 前面有介绍preloader在HPS boot过程中的的作用,接下来讲述下用户在SoCEDS环境下改如何编译preloade ...
- IOS键盘的相关设置(UITextfield)
一.键盘风格 UIKit框架支持8种风格键盘. typedef enum { UIKeyboardTypeDefault, // 默认键盘:支持所有字符 UIKeyboa ...
- (续篇3):飞测独家のJmeter秘籍,限量发放
好东西,分享大家,自上次分享出来fiddler导出jmx格式V4.0版本对外公开后,收到一些反馈,我们利用工作之余时间继续优化,现在一个比较稳定的版本出炉,分享给大伙,我们一起来看看. 特性说明: 版 ...
- [系统] 安装Ubuntu 双系统 - 失败
因为工作原因, 所以需要装ubuntu系统. 在网络上查了一下, 一般都是使用U盘安装. 但是由于手头上既没有U盘又没有光盘,只能用硬盘安装了. 查一下, 使用wubi安装方式从硬盘安装, 非常方便. ...
- 【Unity Shaders】学习笔记——渲染管线
[Unity Shaders]学习笔记——Shader和渲染管线 转载请注明出处:http://www.cnblogs.com/-867259206/p/5595924.html 写作本系列文章时使用 ...
- Drupal8开发教程:模块开发——创建新页面
之前我们已经通过<Drupal8开发教程:认识.info.yml文件>对模块的YAML文件有了了解,今天我们来看如何通过模块开发的方式添加一个新的页面. 在 Drupal 7 中,通过模块 ...
- Github 恶搞教程(一起『玩坏』自己的 Github 吧)
最近在伯乐在线读到一篇趣文,<如何在 Github『正确』做贡献>,里面各种能人恶搞 Github 的『Public contributions』,下面截取几个小伙伴的战绩: 顺藤摸瓜,发 ...
- Do less things
就这样,选择做更少的事情,我觉得挺好,至少能睡得很踏实,吃饭很香,也不会觉得难受! 就这样,节制自己的欲望,但是却能很平静,安安静静走自己的路,我觉得生活有希望,也有快乐! 早上,已经可以八点十分起床 ...
- MSP430F149学习之路——LED
#include <msp430x14x.h> void int_clk(); void delay(int i); void main() { WDTCTL = WDTPW + WDTH ...