LevelDB源码之三SSTable(一)

2015-11-21 01:25:34 · 作者: · 浏览: 6
上一节提到的MemTable是内存表,而当内存表增长到一定程度时(memtable.size> Options::write_buffer_size),会将当前的MemTable数据持久化(LevelDB中实际有两份MemTable,后面LevelDB 数据库备忘时会讲)。持久化的文件(sst文件)称之为Table,LevelDB中的Table分为不同的层级,当前版本的最大层级为7(0-6),table中level0的数据最新,level6的数据最旧。
?
Compaction动作负责触发内存表到SSTable的转换,LOG恢复时也会执行,这里不关心Compaction或恢复的任何细节,后面会单独备忘。
?
?
LevelDB通过BuildTable方法完成SSTable的构建,其创建SSTable文件并将memtable中的记录依次写入文件。BuildTable带了一个输出参数,FileMetaData:
?
 1     struct FileMetaData {
 2         int refs;
 3         int allowed_seeks;          // Seeks allowed until compaction
 4         uint64_t number;
 5         uint64_t file_size;         // File size in bytes
 6         InternalKey smallest;       // Smallest internal key served by table
 7         InternalKey largest;        // Largest internal key served by table
 8 
 9         FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { }
10     };

?

number为一个递增的序号,用于创建文件名,allowed_seeks作者有提到,是当前文件在Compaction到下一级之前允许Seek的次数,这个次数和文件大小相关,文件越大,Compaction之前允许Seek的次数越多,这个Version备忘时也会提。
?
?
?
BuildTable方法中真正做事的时TableBuilder,通过调用Add方法将所有记录添加到数据表中,完成SSTable创建。
?
TableBuilder主要做了如下几件事:
?
创建Index Block:用于Data Block的快速定位
将数据分为一个个的Data Block
如文件需要压缩,执行压缩动作
依次写入Data Block、Meta Block、Index Block、Footer Block,形成完整的SSTable文件结构
其中阶段1-3由Add方法完成,阶段4由Finish方法完成,先来看Add方法:
?
?
 1 void TableBuilder::Add(const Slice& key, const Slice& value) {
 2         Rep* r = rep_;
 3         assert(!r->closed);
 4         if (!ok()) return;
 5         if (r->num_entries > 0) {
 6             assert(r->
options.comparator->Compare(key, Slice(r->last_key)) > 0); 7 } 8 9 //Index Block:Data Block的索引元数据。 10 if (r->pending_index_entry) { 11 assert(r->data_block.empty()); 12 r->options.comparator->FindShortestSeparator(&r->last_key, key); 13 std::string handle_encoding; 14 r->pending_handle.EncodeTo(&handle_encoding); 15 r->index_block.Add(r->last_key, Slice(handle_encoding)); 16 r->pending_index_entry = false; 17 } 18 19 r->last_key.assign(key.data(), key.size()); 20 r->num_entries++; 21 r->data_block.Add(key, value); 22 23 const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); 24 if (estimated_block_size >= r->options.block_size) { 25 Flush(); //超过单数据块大小,写入文件。 26 } 27 }

?

?
Add方法创建Data Block、IndexBlock,DataBlcok通过Flush刷入磁盘文件。
?
再来看Finish方法:
?
?
 1     Status TableBuilder::Finish() {
 2         //Data Block
 3         Rep* r = rep_;
 4         Flush();
 5 
 6         assert(!r->closed);
 7         r->closed = true;
 8         
 9         //Meta Block
10         BlockHandle metaindex_block_handle;
11         BlockHandle index_block_handle;
12         if (ok()) 
13         {
14             BlockBuilder meta_index_block(&r->options);
15             // TODO(postrelease): Add stats and other meta blocks
16             WriteBlock(&meta_index_block, &metaindex_block_handle);
17         }
18 
19         //Index Block
20         if (ok()) {
21             if (r->pending_index_entry) {
22                 r->options.comparator->FindShortSuccessor(&r->last_key);
23                 std::string handle_encoding;
24                 r->pending_handle.EncodeTo(&handle_encoding);
25                 r->index_block.Add(r->last_key, Slice(handle_encoding));
26                 r->pending_index_entry = false;
27             }
28             WriteBlock(&r->index_block, &index_block_handle);
29         }
30 
31         //Footer
32         if (ok()) 
33         {
34             Footer footer;
35             footer.set_metaindex_handle(metaindex_block_handle);    //
36             footer.set_index_handle(index_block_handle);
37             std::string footer_encoding;
38