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++编程规范.目 ...
随机推荐
- setinterval在jQuery里面是怎么使用的。
自动播放?不太明白你的意思啊,自动播放什么呢? 首先jquery选择器获取需要自动播放的元素,然后执行播放动作 例如:window.onload=function(){$("#player& ...
- 安装jdk java -version 不是自己所需要的版本
原服务器安装有1.4的jdk,应用需要安装1.6的jdk.安装完毕后在/etc/profile里配置1.6jdk的环境变量后使用命令java -version显示还是原来的1.4的版本. 解决办法: ...
- Chapter Configuration
Chapter Configuration 在Web.config 或App.config的configuration节,插入如下配置: <configuration> …… <co ...
- 安装和卸载windows程序
安装windows service通常有两种工具 1.Framework目录下的installutil.exe工具.2.visual studio命令行工具 在这里我要说的是当我们使用的系统是64位的 ...
- php 使用pathinfo(), parse_url(), basename()解析URL
本文章向大家介绍解析URL的三种方法,分别为pathinfo()方法.parse_url()方法和basename()方法.每个方法都列举了一个实例,通过实例更容易理解这三个函数的使用方法和技巧,需要 ...
- jdk集合常用方法分析之HashSet和TreeSet
HashSet常用方法介绍 public boolean add(E e) public boolean isEmpty() void clear() public Iterator<E> ...
- 稀疏矩阵乘法加法等的java实现
原创声明:本文系作者原创,转载请写明出处. 一.前言 前几天由于科研需要,一直在搞矩阵的稀疏表示的乘法,不过最近虽然把程序写出来了,还是无法处理大规模的矩阵(虽然已经是稀疏了).原因可能是 ...
- 转——Android应用开发性能优化完全分析
[工匠若水 http://blog.csdn.net/yanbober 转载请注明出处.] 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉 ...
- Flash图表控件FusionCharts自定义图表y轴最大/最小值
自定义图表y轴的最大值和最小值 用户可以使用FusionCharts图表中<chart>元素的yAxisMaxValue和yAxisMinValue属性设置图表限制. 示例: <ch ...
- MSP430F149学习之路——时钟1
1.看门狗产生方波 #include <msp430x14x.h> void main() { WDTCTL = WDT_MDLY_32; IE1 |= WDTIE; P1DIR |= B ...