如果说Protocol Buffers是Google内部表示独立数据记录的单元,那么排序的字符串表--Sorted String Table(SSTable)--是存储,处理和交换数据集的最流行的输出之一。正如名字本身所包含的意思一样,SSTable是一个简单的抽象,用来高效地存储大量的键-值对数据,同时做了优化来实现顺序读/写操作的高吞吐量。

2015.5.28 update 重读了一遍,做了一些小的修改:

SSTable的适用场景:

SSTable的产生背景:

假设我们需要处理输入量级在G或者T字节级别的一系列任务。而且我们需要执行很多步骤,这些步骤是不同的程序执行的--换句话说,假设我们在运行一系列的Map-Reduce任务。鉴于输入的数据量级很大,所以读取和写入数据就能够占运行时间的大头。因此,就不考虑随机读取和写入的情况了,相反我们流式处理输入数据,一旦处理完成,同样利用流式操作把结果数据写回到磁盘上。这样,我们可以摊薄磁盘I/O操作的成本

所以SSTable是一个简单,但是非常有用的用来交换大量的、排好序的数据片段的数据结构。它的使用场景是:

  • 需要高效地存储大量的键-值对数据
  • 数据是顺序写入
  • 要求高效地顺序读写
  • 没有随机读取或者对随机读取性能要求不高

SSTable简介:

Google Bigtable论文中对SSTable的介绍:

SSTable提供一个可持久化[persistent],有序的、不可变的从键到值的映射关系,其中键和值都是任意字节长度的字符串。SSTable提供了以下操作:按照某个键来查询关联值,可以指定键的范围,来遍历其中所有的键值对。每个SSTable内部由一系列块(block)组成(通常每块大小为64KB,是可配置的)。使用存储在SSTable结尾的块索引(block index)来定位块;当SSTable打开时,索引会被加载到内存里。一次磁盘寻道(disk seek)就可以完成查询(lookup)操作:首先通过二分查找在存储在内存的索引中找到对应的块,然后从磁盘上读取这块内容。SSTable也可以完整地映射到内存里,这样在执行查询和扫描(scan)的时候就不用操作磁盘了.

所以可以简单的总结:

SSTable是一个键是有序的,存储字符串形式键值对的文件。

SSTable的设计:

"Sorted String Table"就如名字所言,它是一个内部包含了任意长度、排好序的键值对<key,value>集合的文件。其结构如上图所示,SSTable文件由两部分数据组成:索引和键值对数据。所有的key和value都是紧凑地存放在一起的,如果要读取某个键对应的值,需要通过索引中的key:offset来定位。

从上图可以看到,因为SSTable文件中所有的键值对<key,value>是存放到一起的,所以SSTable在序列化成文件之后,是不可变的,因为此时的SSTable,就类似于一个数组一样,如果插入或者删除,需要移动一大片数据,开销比较大。

顺序读取整个文件,就拿到了一个排好序的索引。如果文件很大,还可以为了加速访问而追加或者单独建立一个独立的key:offset的索引。

leveldb中,SSTable的实现

leveldb/目录是存放对外开放的API头文件的目录,对作用域等做了严格的限制,为了避免引入多余的依赖关系,比较多的使用了类和结构体的前置声明[forward declaration]。

SSTable对应的实现是Table类,头文件是:include/leveldb/table.h。通过Table类开头的注释可以看到Table是不可变的,可持久化的。SSTable由于是不可改变的,只读的,所以是线程安全的,不需要外界的同步操作。

Table对外接口:

Table类只提供了简单的3个操作:

  1. 通过文件来反序列化,读取SSTable的数据:static Status Open(const Options& options, RandomAccessFile* file, uint64_t file_size, Table** table);
  2. 获得用来访问SSTable数据的迭代器:Iterator* NewIterator(const ReadOptions&) const SSTable的数据读取都是通过迭代器进行的,迭代器也只允许读取操作,没有提供写入操作。
  3. 预估key[可能还没有写入到SSTable中]对应的数据存储到SSTable文件的偏移:uin64_t ApproximateOffsetOf(const Slice& key) const

    leveldb对外提供了GetApproximateSizes()--通过指定key的范围来获取存储这些数据的文件大致大小的功能,所以需要底层的这些数据结构也来提供对应的功能。同时这类函数也能提高leveldb系统的可测性,通过文件的大小就可以判断写入数据是否正常。

可以看到SSTable的拷贝构造函数Table(const Table)和赋值函数void operator=(const Table&)都是私有的,这样就是禁止SSTable对象的拷贝了。Table类的使用方,只能通过Open接口来反序列化SSTable对象。

Table需要知道的类和结构体

通过table.h头文件可以看到它需要打交道的类或者结构体主要有:

  • class Block:

    上文提到每个SSTable文件由一系列可配置大小的块(block)组成。Block就是对block块数据的封装,对外提供size()和迭代器Iterator接口。

  • class BlockHandle

    定义在table/format.h中,代表了存储数据的文件的范围:偏移offset+大小size

  • class Footer:

    定义在table/format.h中,封装了在每个SSTable文件尾部存储的固定大小的元数据信息(metadata),包含了两部分数据:metaindex和index数据。index数据就是上文中提到的SSTable的索引数据,而metaindex存储的是过滤器(例如布隆过滤器)的信息。利用过滤器,可以显著地减少磁盘访问。

  • class RandomAccessFile:

    SSTable关联的文件,是可以随机读取的文件:可以指定从哪里开始读,读取多少字节,方便SSTable按照需要去读取block的数据

  • struct ReadOptions, Struct Options

    把一堆选项相关的参数定义到结构体里,方便传递参数,也方便理解,否则看到的就是一堆参数了。

  • class TableCache

    一个SSTable的缓存(cache),每次需要对某个SSTable文件要做读取操作时,去对应的TableCache里面进行操作[如果没有命中缓存,会加载这个SSTable数据并更新缓存]。TableCache里面包含了SSTable的全部内容:索引+数据

  • struct Table::Rep

    定义在table.cc中[是Table类内部使用的结构体],存储了SSTable相关的一些元数据,例如当前SSTable实例对应的文件句柄[file]、在Table缓存中的句柄[cache_id]、过滤器的读取对象[filter]、过滤器的数据[filter_data]、元索引[metaindex_handle]和索引[index]数据。有了这些数据,就可以唯一地代表一个SSTable的数据了。

单独说一下迭代器Iterator,此接口类提供了丰富的数据访问操作,所有对SSTable和SSTable中block的读取操作都用迭代器来进行。迭代器定义在include/leveldb/iterator.h中,这里也只是定义了一个迭代器的接口类,规定了对外的统一接口函数,这些接口函数都是纯虚函数,需要子类去实现。在leveldb中可以看到很多这样的例子,这就是面向接口编程的思想。通过迭代器类Iterator的定义看到,table类对外的数据访问只能通过迭代器类Iterator来进行,而且迭代器只提供读取操作,key()和value()函数都是const类型,不允许修改Iterator类内部的数据。迭代器还提供了RegisterCleanup函数,可以用挂接多个CleanupFunction类型的回调函数并自定义两个参数。CleanupFunction是用来在迭代器销毁时,做自定义的清理工作。

从format.h中还可以看到Table定义了Table内部使用的类和结构体:

  • struct BlockContents

    封装了SSTable中每一个block的数据信息,包含实际存储的数据(Slice data), 是否可以缓存(bool cacheable), 是否需要调用方释放存储数据的内存这三类信息。

SSTable的特点

* 存储的是<键,值>格式的字节数据
* 字节数据的长度随意,没有限制
* 键可以重复, 键值对不需要对齐
* 随机读取操作非常高效

SSTable的限制

*	一旦SSTable写入硬盘后,就是不可变的,因为插入或者删除需要对SSTable文件进行大量的I/O操作
* 不适合随机读取和写入,因为效率很低,原因同上一条

关于SSTable的设计,还有一些东西没有介绍,例如在磁盘上存储的具体格式,如何序列化等,留待下一篇介绍。

回到本系列目录:leveldb源码学习系列

SSTable 介绍(一)的更多相关文章

  1. SSTable 介绍(二)

    作者:Jack47 上一篇SSTable 介绍(一)介绍了SSTable的适用场景和leveldb中SSTable的设计.本篇介绍SSTable文件的结构组成. SSTable的特点 首先明确一下上文 ...

  2. leveldb源码学习系列

    楼主从2014年7月份开始学习<>,由于书籍比较抽象,为了加深思考,同时开始了Google leveldb的源码学习,主要是想学习leveldb的设计思想和Google的C++编程规范.目 ...

  3. leveldb 学习记录(五)SSTable格式介绍

    本节主要记录SSTable的结构 为下一步代码阅读打好基础,考虑到已经有大量优秀博客解析透彻 就不再编写了 这里推荐 https://blog.csdn.net/tankles/article/det ...

  4. leveldb源码分析--SSTable之Compaction

    对于compaction是leveldb中体量最大的一部分,也应该是最为复杂的部分,为了便于理解我们首先从一些基本的概念开始.下面是一些从doc/impl.html中翻译和整理的内容: Level 0 ...

  5. LevelDb简单介绍和原理——本质:类似nedb,插入数据文件不断增长(快照),再通过删除老数据做更新

    转自:http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html 有时间再好好看下整个文章! 说起LevelDb也许您不清楚,但是如果作 ...

  6. LevelDB系列之SSTable(Sorted Strings Table)文件

    SSTable是Bigtable中至关重要的一块,对于LevelDb来说也是如此,对LevelDb的SSTable实现细节的了解也有助于了解Bigtable中一些实现细节. 本节内容主要讲述SSTab ...

  7. leveldb 学习记录(六)SSTable:Block操作

    block结构示意图 sstable中Block 头文件如下: class Block { public: // Initialize the block with the specified con ...

  8. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  9. leveldb源码分析--SSTable之TableBuilder

    上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...

随机推荐

  1. pll及其modesim仿真

    100mhz输入时钟,pll层40mhz和200zhm 进入quartus,建立工程,新建图形文件,导入pll模块,设定pll相关参数.   完成pll模块的建立,并生成pll.v文件 建立modes ...

  2. 动态加载jar包中的类(方式一)

    嘛, 直接上代码 public static class TestClassLoader extends ClassLoader { @Override protected Class<?> ...

  3. linux命令连接远程服务器

    ssh root@IP    ***连接远程ip cd 路径           ***进入指定路径 ll                   ***查看当前文件 ll -a             ...

  4. oracle 创建用户和imp指定表空间

    创建用户: 1,sqlplus sys/pwd as sysdba; 2, create user username identified by password; 3, grant dba,conn ...

  5. 了解Hadoop和大数据

    1. 场景: 现在人产生数据越来越快,机器则更快,所以需要另外的一种处理数据的方法.   硬盘容量增加,但是性能没跟上,解决办法是将数据分到多块硬盘,然后同时读取. 问题:     硬件问题 -- 复 ...

  6. 解决jquery1.9不支持browser对象的问题||TypeError: $.browser is undefined

    在插件的源代码里插入如下代码: (function(jQuery){ if(jQuery.browser) return; jQuery.browser = {}; jQuery.browser.mo ...

  7. C语言小练习二

    题目要求: 编程实现功能:将4行4列数组的左下三角设置为下图所示的数据.43 7 2 6 91 5 8 10 程序源码: #include <stdio.h> int main(void) ...

  8. 通过统计用户DNS解析记录,实现监控用户上网行为

    上次通过扫描抓包分析TTL的方式检测公司网络开放的端口,发现没有开放53端口(DNS),也就是在公司内部的主机只能用服务器自动分配的DNS,并且发现这是台内部服务器.今天发现bing上不去,检测后发现 ...

  9. 数据契约(DataContract)

    原文地址:http://www.cnblogs.com/Gavinzhao/archive/2010/06/01/1748736.html 服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务 ...

  10. linux系统启动时更改MAC地址

    vim /etc/rc.local #change the server's MAC address("00:50:56:84:5C:76" change to 00:50:56: ...