“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;
}

备忘如下:

  1. Log文件被逻辑上分为多个Block,每个Block大小为32K。
  2. 每条记录由Record Header + Record Content组成,其中Header大小为kHeaderSize(7字节)。
  3. 当前Block剩余大小不足以填充Record Header时,以"\x00\x00\x00\x00\x00\x00"占位。
  4. 当Block无法完整记录一条Record时,通过type信息标识该record在当前block中的区块信息,以便读取时可根据type拼接出完整的record。
  5. EmitPhysicalRecord向Block中插入Record数据
  6. 每个Record结构如下:

Header

Record Content

CRC

Record Size

Type

Record Content

读取

Log读取逻辑并无特别之处,略。

注:LOG文件和活动的MemTable对应,当活动的MemTable将被切换为非活动(immutable)MemTable时,将创建新的LOG文件。

原本Current、Manifest与Log打算一起备注,但要搞清楚Manifest,LevelDB的版本机制必定要搞清楚,而这本身又是很丰富的内容。

LevelDB源码之四LOG文件的更多相关文章

  1. LevelDB源码之五Current文件\Manifest文件\版本信息

    版本信息有什么用?先来简要说明三个类的具体用途: Version:代表了某一时刻的数据库版本信息,版本信息的主要内容是当前各个Level的SSTable数据文件列表. VersionSet:维护了一份 ...

  2. LevelDB源码剖析

    LevelDB的公共部件并不复杂,但为了更好的理解其各个核心模块的实现,此处挑几个关键的部件先行备忘. Arena(内存领地) Arena类用于内存管理,其存在的价值在于: 提高程序性能,减少Heap ...

  3. leveldb源码分析--日志

    我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...

  4. leveldb源码分析--Key结构

    [注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...

  5. leveldb 源码--总体架构分析

    一 本文目的 对leveldb的总体设计框架分析(关于leveldb基本原理,此文不做阐述,读者可以自行检索文章阅读即可),对leveldb中底层数据存储数据格式,内存数据模型,compact,版本管 ...

  6. Leveldb源码分析--1

    coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...

  7. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  8. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  9. leveldb源码学习系列

    楼主从2014年7月份开始学习<>,由于书籍比较抽象,为了加深思考,同时开始了Google leveldb的源码学习,主要是想学习leveldb的设计思想和Google的C++编程规范.目 ...

随机推荐

  1. setinterval在jQuery里面是怎么使用的。

    自动播放?不太明白你的意思啊,自动播放什么呢? 首先jquery选择器获取需要自动播放的元素,然后执行播放动作 例如:window.onload=function(){$("#player& ...

  2. 安装jdk java -version 不是自己所需要的版本

    原服务器安装有1.4的jdk,应用需要安装1.6的jdk.安装完毕后在/etc/profile里配置1.6jdk的环境变量后使用命令java -version显示还是原来的1.4的版本. 解决办法: ...

  3. Chapter Configuration

    Chapter Configuration 在Web.config 或App.config的configuration节,插入如下配置: <configuration> …… <co ...

  4. 安装和卸载windows程序

    安装windows service通常有两种工具 1.Framework目录下的installutil.exe工具.2.visual studio命令行工具 在这里我要说的是当我们使用的系统是64位的 ...

  5. php 使用pathinfo(), parse_url(), basename()解析URL

    本文章向大家介绍解析URL的三种方法,分别为pathinfo()方法.parse_url()方法和basename()方法.每个方法都列举了一个实例,通过实例更容易理解这三个函数的使用方法和技巧,需要 ...

  6. jdk集合常用方法分析之HashSet和TreeSet

    HashSet常用方法介绍 public boolean add(E e) public boolean isEmpty() void clear() public Iterator<E> ...

  7. 稀疏矩阵乘法加法等的java实现

    原创声明:本文系作者原创,转载请写明出处. 一.前言       前几天由于科研需要,一直在搞矩阵的稀疏表示的乘法,不过最近虽然把程序写出来了,还是无法处理大规模的矩阵(虽然已经是稀疏了).原因可能是 ...

  8. 转——Android应用开发性能优化完全分析

    [工匠若水 http://blog.csdn.net/yanbober 转载请注明出处.] 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉 ...

  9. Flash图表控件FusionCharts自定义图表y轴最大/最小值

    自定义图表y轴的最大值和最小值 用户可以使用FusionCharts图表中<chart>元素的yAxisMaxValue和yAxisMinValue属性设置图表限制. 示例: <ch ...

  10. MSP430F149学习之路——时钟1

    1.看门狗产生方波 #include <msp430x14x.h> void main() { WDTCTL = WDT_MDLY_32; IE1 |= WDTIE; P1DIR |= B ...