如果说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. php设计模式 Proxy (代理模式)

    代理,指的就是一个角色代表另一个角色采取行动,就象生活中,一个红酒厂商,是不会直接把红酒零售客户的,都是通过代理来完成他的销售业务.而客户,也不用为了喝红酒而到处找工厂,他只要找到厂商在当地的代理就行 ...

  2. Linux网络栈下两层实现

    http://www.cnblogs.com/zmkeil/archive/2013/04/18/3029339.html 1.1简介 VLAN是网络栈的一个附加功能,且位于下两层.首先来学习Linu ...

  3. 解决root用户ssh配置无密码登陆/hadoop用户照仿可以实现相同功能:hadoop用户登录并且把命令的所有root换成home/hadoop

    http://inuyasha1027.blog.51cto.com/4003695/1132896/ 主机ip:192.168.163.100(hostname: node0) ssh无密码登陆的远 ...

  4. mac上的git completion

    只安装bash-completion,是没有git补全的,因为此时/usr/local/etc/bash-completion.d/下面没有git-XXX.sh 解决方法是brew install g ...

  5. url中,中文乱码的问题

    1// 在jsp中 String add = java.net.URLEncoder.encode("添加", "utf-8"); add = java.net ...

  6. tomcat使用线程池配置高并发连接

    1:配置executor属性打开/conf/server.xml文件,在Connector之前配置一个线程池:[html] view plain copy<Executor name=" ...

  7. 菜单的隐藏&显示

    //还是先上万一老师的原代码 //隐藏与显示菜单Self.Menu := nil; {隐藏菜单} Self.Menu := MainMenu1; {显示菜单} //初一看到代码,我有点抓不到感觉的意思 ...

  8. yii安装 /You don't have permission to access on this server

    在安装yii的时候 ,当打开了init.bat进行配置的时候小黑本弹出了个小黑框立刻就关闭了,  进入cmd模式再打开init.bat就出现了"You don't have permissi ...

  9. 通过json数据进行传递调用

    最近在弄andriod的程序,需要调用web服务器上的数据,服务采用C#写的,并部署在iis服务器上.我们可以像.NET那样调用服务那,利用andriod库自带的HttpPost和HttpGet类来调 ...

  10. JQuery FullCalendar(二)

    前言:根据前文介绍,我们对JQuery FullCalendar如何从后台取数据有了初步了解,已经实现最基本的要求.下面介绍一下FullCalendar的事件 $('#calendar').fullC ...