标签:
“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)还会设计随机读操作,作者针对每种操作类型做了封装:
1 class SequentialFile { 2 public: 3 virtual Status Read(size_t n, Slice* result, char* scratch) = 0; 4 virtual Status Skip(uint64_t n) = 0; 5 }; 6 class RandomAccessFile { 7 public: 8 virtual Status Read(uint64_t offset, size_t n, Slice* result, 9 char* scratch) const = 0; 10 }; 11 class WritableFile { 12 public: 13 virtual Status Append(const Slice& data) = 0; 14 virtual Status Close() = 0; 15 virtual Status Flush() = 0; 16 virtual Status Sync() = 0; 17 };
操作系统相关操作在不同系统下,实现各不相同,作者也做了统一的接口封装(跨平台),称之为“环境类”Env:
1 class Env { 2 public: 3 Env() { } 4 virtual ~Env(); 5 6 // Return a default environment suitable for the current operating 7 // system. 8 static Env* Default(); 9 10 //基于不同的操作系统实现了各种文件类型的具体类,通过该工厂方法返回。 11 virtual Status NewSequentialFile(const std::string& fname, SequentialFile** result) = 0; 12 virtual Status NewRandomAccessFile(const std::string& fname, RandomAccessFile** result) = 0; 13 virtual Status NewWritableFile(const std::string& fname, WritableFile** result) = 0; 14 ...... 15 };
写入
Leveldb命名空间下,有一个名为log的子命名空间,其下有Writer、Reader两个实现类。按前几节的命名规则,Writer其实是一个Builder,它对外提供了唯一的AddRecord方法用于追加操作记录。
1 Status Writer::AddRecord(const Slice& slice) { 2 const char* ptr = slice.data(); 3 size_t left = slice.size(); 4 5 // Fragment the record if necessary and emit it. Note that if slice 6 // is empty, we still want to iterate once to emit a single 7 // zero-length record 8 Status s; 9 bool begin = true; 10 do { 11 const int leftover = kBlockSize - block_offset_; //1. 当前块剩余大小 12 assert(leftover >= 0); 13 if (leftover < kHeaderSize) //2. 剩余大小不足,占位 14 { 15 // Switch to a new block 16 if (leftover > 0) 17 { 18 // Fill the trailer (literal below relies on kHeaderSize being 7) 19 assert(kHeaderSize == 7); 20 dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); 21 } 22 block_offset_ = 0; 23 } 24 25 // Invariant: we never leave < kHeaderSize bytes in a block. 26 assert(kBlockSize - block_offset_ - kHeaderSize >= 0); 27 28 const size_t avail = kBlockSize - block_offset_ - kHeaderSize; 29 const size_t fragment_length = (left < avail) ? left : avail; //3. 当前块存储的空间大小 30 31 RecordType type; //4. Record Type 32 const bool end = (left == fragment_length); 33 if (begin && end) { 34 type = kFullType; 35 } 36 else if (begin) { 37 type = kFirstType; 38 } 39 else if (end) { 40 type = kLastType; 41 } 42 else { 43 type = kMiddleType; 44 } 45 46 s = EmitPhysicalRecord(type, ptr, fragment_length); //5. 写入文件 47 ptr += fragment_length; 48 left -= fragment_length; 49 begin = false; 50 } while (s.ok() && left > 0); 51 return s; 52 }
备忘如下:
Header |
Record Content |
||
CRC |
Record Size |
Type |
Record Content |
读取
Log读取逻辑并无特别之处,略。
原本Current、Manifest与Log打算一起备注,但要搞清楚Manifest,LevelDB的版本机制必定要搞清楚,而这本身又是很丰富的内容。
标签:
原文地址:http://www.cnblogs.com/desmondwang/p/4817798.html