leveldb学习:sstable(2)
block写入:block_builder
block.h和.cc里定义了block的entry存储格式和restart,提供了entry的查找接口以及迭代器。那么怎样往写block里写entry呢?leveldb遵循面向对象的设计理念在block_builder类里提供了相关接口。
BlockBuilder相关函数:
- Add( )将entry顺序写入现有block数据块的末端,排序工作在上层的函数完毕。
- Finish( )。当block写满,完毕写入重新启动点数组和重新启动点个数的写入
- Reset( )。重置block
sstable
已经知道。sstable是leveldb中持久化数据的文件格式。而总体来看。sstable由数据(data)和元信息(meta/index)组成,数据和源信息统一以block单位存储。读取时也按统一的逻辑读取,总体的数据格式例如以下:
- data_block:实际存储的kv数据
- meta_block:当前版本号未实现
- index_block:保存每一个data_block的last_key及其在sstable文件里的索引
sstable读取:table
/table/table.cc是有关将sstable读取的操作:
private:
struct Rep;
Rep* rep_;
定义了结构rep。并在table类设立一个指针成员。并在table::open( )函数完毕了rep_的实例化
Rep* rep = new Table::Rep;
rep结构:
struct Table::Rep {
~Rep() {
delete filter;
delete [] filter_data;
delete index_block;
}
Options options;//用户设置
Status status;//状态
RandomAccessFile* file;//文件读操作流。主要成员有文件的名字,i节点和读操作
uint64_t cache_id;
FilterBlockReader* filter;//和meta_block有关。不用管
const char* filter_data;//
BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer
Block* index_block;
};
BlockHandle是一个用来指向block在文件里位置的“指针”(里面记录的是文件偏移量),可參考format.h;
footer:文件末尾的固定长度的数据。保存着metaindex_block和index_block的索引信息(blockHandle),最后有8字节的magic校验。
显然footer信息的读取对掌握整个table至关重要。
在table::open( )函数中就会从文件的末尾读取footer:
......
Slice footer_input;
Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength,
&footer_input, footer_space);
if (!s.ok()) return s;
Footer footer;
s = footer.DecodeFrom(&footer_input);
if (!s.ok()) return s;
......
Block* index_block = NULL;
if (s.ok()) {
s = ReadBlock(file, opt, footer.index_handle(), &contents);
if (s.ok()) {
index_block = new Block(contents);
}
}
......
rep->file = file;
rep->metaindex_handle = footer.metaindex_handle();
rep->index_block = index_block;
readBlock就是通过blockhandle读取文件里指定block的函数,定义在format.cc
Status ReadBlock(RandomAccessFile* file,
const ReadOptions& options,
const BlockHandle& handle,
BlockContents* result) {
result->data = Slice();
result->cachable = false;
result->heap_allocated = false;
//blockcontents的初始化
// Read the block contents as well as the type/crc footer.
// See table_builder.cc for the code that built this structure.
size_t n = static_cast<size_t>(handle.size());
char* buf = new char[n + kBlockTrailerSize];
Slice contents;
Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf);
if (!s.ok()) {
delete[] buf;
return s;
}
if (contents.size() != n + kBlockTrailerSize) {
delete[] buf;
return Status::Corruption("truncated block read");
}
// do something
return Status::OK();
}
kBlockTrailerSize就是每一个block末端的五字节信息,包含压缩标志位和用于CRC校验的开销。do something 就是对提取到的内容分析。推断有无压缩。错误时返回状态信息以及赋值result。
sstable写入:table_builder
sstable写如不须要关心排序,由于sstable的产生是由memtable dump或者compact时merge排序产生的,key的顺序上层已经保证。
结构rep:
struct TableBuilder::Rep {
Options options;
Options index_block_options;
WritableFile* file;//封装了流操作的文件
uint64_t offset;//写入位置的偏移量
Status status;
BlockBuilder data_block; // 用于将entry写入当前data_block
BlockBuilder index_block;// 用于在index_block加入data_block的索引信息
std::string last_key; //当前table中最后条目的key。写入key要大于此。否则上层未提供排好序的entry
int64_t num_entries; //条目总数
bool closed; //table关闭标志位。 Either Finish() or Abandon() has been called.
FilterBlockBuilder* filter_block;
bool pending_index_entry;//当前block为空时为true
BlockHandle pending_handle; // Handle to add to index block
std::string compressed_output;
Rep();
}
void TableBuilder::Add(const Slice& key, const Slice& value)
{
Rep* r = rep_;
assert(!r->closed);
if (!ok()) return;
if (r->num_entries > 0) {
assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0);
}
......
r->last_key.assign(key.data(), key.size());
r->num_entries++;
r->data_block.Add(key, value);
const size_t estimated_block_size = r->data_block.CurrentSizeEstimate();
if (estimated_block_size >= r->options.block_size) {
Flush();
}
}
leveldb把数据dump到磁盘,在内存中仅仅有一份block。当block满了(大于options.block_size),就自己主动将此block写入磁盘(Flush)。
写入操作调用层次:
- Add( ),写入内存中的block,推断block大小。决定是否写入磁盘
- Flush( )
- WriteBlock( ),取block压缩标志位决定是否压缩。写入压缩标志位
- WriteRawBlock( ),加入CRC,调用文件流写入磁盘
leveldb学习:sstable(2)的更多相关文章
- leveldb 学习记录(五)SSTable格式介绍
本节主要记录SSTable的结构 为下一步代码阅读打好基础,考虑到已经有大量优秀博客解析透彻 就不再编写了 这里推荐 https://blog.csdn.net/tankles/article/det ...
- leveldb学习:Versionedit和Versionset
VersionEdit: compact过程中会有一系列改变当前Version的操作(FileNumber添加.删除input的sstable,添加输出的sstable).为了缩小version切换的 ...
- LevelDB学习笔记 (3): 长文解析memtable、跳表和内存池Arena
LevelDB学习笔记 (3): 长文解析memtable.跳表和内存池Arena 1. MemTable的基本信息 我们前面说过leveldb的所有数据都会先写入memtable中,在leveldb ...
- LevelDB 学习笔记1:布隆过滤器
LevelDB 学习笔记1:布隆过滤器 底层是位数组,初始都是 0 插入时,用 k 个哈希函数对插入的数字做哈希,并用位数组长度取余,将对应位置 1 查找时,做同样的哈希操作,查看这些位的值 如果所有 ...
- LevelDB 学习笔记2:合并
LevelDB 学习笔记2:合并 部分图片来自 RocksDB 文档 Minor Compaction 将内存数据库刷到硬盘的过程称为 minor compaction 产出的 L0 层的 sstab ...
- leveldb 学习记录(三) MemTable 与 Immutable Memtable
前文: leveldb 学习记录(一) skiplist leveldb 学习记录(二) Slice 存储格式: leveldb数据在内存中以 Memtable存储(核心结构是skiplist 已介绍 ...
- leveldb 学习记录(四) skiplist补与变长数字
在leveldb 学习记录(一) skiplist 已经将skiplist的插入 查找等操作流程用图示说明 这里在介绍 下skiplist的代码 里面有几个模块 template<typenam ...
- leveldb 学习记录(四)Log文件
前文记录 leveldb 学习记录(一) skiplistleveldb 学习记录(二) Sliceleveldb 学习记录(三) MemTable 与 Immutable Memtablelevel ...
- leveldb学习:DBimpl
leveldb将数据库的有关操作都定义在了DB类,它负责整个系统功能组件的连接和调用.是整个系统的脊柱. level::DB是一个接口类,真正的实如今DBimpl类. 作者在文档impl.html中描 ...
- LevelDB学习笔记 (1):初识LevelDB
LevelDB学习笔记 (1):初识LevelDB 1. 写在前面 1.1 什么是levelDB LevelDB就是一个由Google开源的高效的单机Key/Value存储系统,该存储系统提供了Key ...
随机推荐
- 【Xcode学C-1】怎样用Xcode练习C语言,并练习一个输出样例,以及重要的注意事项
直接用Xcode学习C语言,为iOS开发打基础. (1)选择OS X >>> Application >>> Command Line Tool (2)输入产品名称 ...
- java基础——各种变量你晕了不?
java 中的变量大致分为 成员变量 和 局部变量 两大类. 成员变量: 在类体里面定义的变量称为成员变量. 假设该成员变量有 static keyword修饰.则该成员变量称为 静态 ...
- Android学习笔记(十二)——使用意图传递数据的几种方式
使用意图传递数据的几种方式 点此获取完整代码 我们除了要从活动返回数据,也经常要传递数据给活动.对此我们能够使用Intent对象将这些数据传递给目标活动. 1.创建一个名为PassingData的项目 ...
- Swift - 获取应用名称、应用版本、设备型号、系统版本等信息
有时我们在 App 中提交一些统计信息或者用户反馈信息时,为了能更好地进行分析,通常会附带上当前应用程序的名称.版本号.设备型号.以及设备系统版本.下面演示如何获取这些信息. 1,效果图 程序启动后自 ...
- 机器学习案例学习【每周一例】之 Titanic: Machine Learning from Disaster
下面一文章就总结几点关键: 1.要学会观察,尤其是输入数据的特征提取时,看各输入数据和输出的关系,用绘图看! 2.训练后,看测试数据和训练数据误差,确定是否过拟合还是欠拟合: 3.欠拟合的话,说明模 ...
- malloc和new出来的地址都是虚拟地址 你就说内存管理单元怎么可能让你直接操作硬件内存地址!
malloc的实现与物理内存自然是无关的,内核为每个进程维护一张页表,页表存储进程空间内每页的虚拟地址,页表项中有的虚拟内存页对应着某个物理内存页面,也有的虚拟内存页没有实际的物理页面对应.无论mal ...
- JSP页面动态查询添加数据与分页数据显示
1 . <%@ page language="java" contentType="text/html; charset=UTF-8"%> < ...
- [转]数据库事务中的隔离级别和锁+spring Transactional注解
数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...
- ListView的Item动画
1.效果图 2.需求就是点击item歌曲时,实现一种飞入到预约按钮处的效果 3.思路:在布局文件中加入了一个条目布局,和listView的item一样,点击listView的item时,使用给条目布局 ...
- struts2的DTD配置文件
新手可以看看,高手可以跳过…… 最近在学习struts2这个框架,自己也动手写过一些DTD文件,所以很好struts2这个DTD文件是怎么写的,接下来就一个一个的分析 根元素是struts,然后又4个 ...