leveldb之log写操作
来源:互联网 发布:网易云课堂mac版 编辑:程序博客网 时间:2024/05/23 17:55
当应用要插入一条记录时,leveldb首先是将其写入到log中,若成功,则继续将其插入到memtable中。因此,当系统故障而memtable又没有来得及将数据存放到内存中,那么就可以通过log文件来恢复数据,保证数据不会丢失。
由于log的读比较复杂,因此将主要介绍log的写操作。
在class DBImpl中主要有两个与log相关的成员变量:log::Writer* log_; 和 WritableFile* logfile_;
其中log_用于向logfile_中增加一条记录 ,logfile_主要用于对log文件进行同步、刷新等操作
1、Writer类
class Writer { public: explicit Writer(WritableFile* dest); ~Writer(); Status AddRecord(const Slice& slice); private: WritableFile* dest_; //以一个WritableFile对象作为Writer的成员,Writer则是将要插入的记录插入到dest_中 int block_offset_; // 当前位置在Block中的偏移 uint32_t type_crc_[kMaxRecordType + 1];//CRC Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);//调用Append写入数据};
Writer类对外只提供了一个方法AddRecord()用于加入一条记录,同时其中还有一个WritableFile成员变量,记录最终是插入到WritableFile创建的文件中的。
根据leveldb源码可知,log文件每次都是以32K的物理Block为单位进行操作的,因此log文件可看作是由很多个连续的32K的Block组成的。插入一条数据时,首先确定数据在Block中的起始位置,然后不断写入到log文件中。
Status Writer::AddRecord(const Slice& slice) { const char* ptr = slice.data(); size_t left = slice.size(); Status s; bool begin = true; do { const int leftover = kBlockSize - block_offset_;//当前Block中的剩余空间 assert(leftover >= 0); if (leftover < kHeaderSize) {//若剩余空间比固定头部要小,则要在一个新Block的开始写入数据 if (leftover > 0) { // Fill the trailer (literal below relies on kHeaderSize being 7) assert(kHeaderSize == 7); dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));//尾部填充 } block_offset_ = 0;//块内偏移置0 } const size_t avail = kBlockSize - block_offset_ - kHeaderSize;//当前Block中,可用于填充数据的长度 const size_t fragment_length = (left < avail) ? left : avail;//第一个要写入Block的分段的长度 RecordType type; const bool end = (left == fragment_length);//若end=1,则表明所有数据都可存放在当前Block中 if (begin && end) {//根据begin和end确定当前记录的类型type type = kFullType; } else if (begin) { type = kFirstType; } else if (end) { type = kLastType; } else { type = kMiddleType; } s = EmitPhysicalRecord(type, ptr, fragment_length);//将固定头部和fragment_length长的分段写入到log文件dest_中 ptr += fragment_length;//指向数据的指针向前移动已写入长度 left -= fragment_length;//剩余待写入长度减小 begin = false; } while (s.ok() && left > 0); return s;}
要写入的记录分为固定头部和待写入数据两部分,其中固定头部包括:CRC(4字节)、记录长度(2字节)、type(1字节)共7字节。而待写入数据一般是经过WriteBatch组织的一条记录(主要包括type(kTypeValue或kTypeDeletion)、key、value)。
2、WritableFile类
class WritableFile { public: WritableFile() { } virtual ~WritableFile(); virtual Status Append(const Slice& data) = 0;//写入记录 virtual Status Close() = 0;//关闭文件 virtual Status Flush() = 0;//刷新文件 virtual Status Sync() = 0;//同步文件};
WritableFile类只是作为一个抽象基类,定义了一些纯虚函数作为接口,最终作为父类被继承。
leveldb中定义的一个子类为class PosixWritableFile:
class PosixWritableFile : public WritableFile { private: std::string filename_;//要操作的文件名 FILE* file_;//最终要操作的文件 public: virtual Status Append(const Slice& data) { size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);//调用fwrite将数据写入到file_中 return Status::OK(); } virtual Status Close() { Status result; if (fclose(file_) != 0) {//关闭文件 result = IOError(filename_, errno); } file_ = NULL; return result; } virtual Status Flush() { if (fflush_unlocked(file_) != 0) {//刷新文件 return IOError(filename_, errno); } return Status::OK(); } virtual Status Sync() {//同步文件 // Ensure new files referred to by the manifest are in the filesystem. Status s = SyncDirIfManifest(); if (fflush_unlocked(file_) != 0 || fdatasync(fileno(file_)) != 0) { s = Status::IOError(filename_, strerror(errno)); } return s; }};
WritableFile类在写入数据时不会对数据进行任何封装、修改操作,而是直接将数据写入到log文件中。
因此我们一般插入一个Key-Value对时,首先会调用batch.Put(key, value);将其组织成一条记录,然后调用Write::AddRecord(),在log文件中找到合适的位置,同时为每一条记录增加一个头部,再将其写入到log文件中。然后调用WritableFile的Flush()、Sync()等方法来对log文件进行操作。
- leveldb之log写操作
- leveldb之log文件
- levelDB之三:log文件
- leveldb设计分析之log
- leveldb(八):log::Writer写日志
- LevelDB Log
- LevelDb日知录之三 log文件
- leveldb之Put、Get操作
- leveldb之Put、Get操作
- leveldb:write(如何处理并发写操作)
- LevelDb日知录之八:Compaction操作
- leveldb(十一):SSTable之2写sstable文件
- leveldb之Compaction操作上之调用流程
- leveldb之Compaction操作下之具体实现
- leveldb之Compaction操作下之具体实现
- levelDB源码分析-Log文件
- log::Writer-levelDB源码解析
- log::Reader-levelDB源码解析
- Mysql 源码安装
- 黑马程序员--关于Java的继承,多态的个人笔记
- 自定义控件实现锁屏效果!
- 建一个maven WEB项目
- 京东高调开售三星SUHD TV,从“真”向“壕”转型?
- leveldb之log写操作
- Windows Phone Gird布局
- 【POJ】3667-Hotel(线段树的区间合并)
- Robotium源码分析之运行原理
- 使用红杏公益代理,更新Android Studio,SDK以及Gradle等
- 获取SSDT服务表数据
- STL组件——算法
- Java多线程理解
- windows 8.1 IIS8 发布MVC5项目时,提示“该操作需要IIS集成管线模式”的可能的一种解决办法