SSTable 介绍(二)
作者:Jack47
上一篇SSTable 介绍(一)介绍了SSTable的适用场景和leveldb中SSTable的设计。本篇介绍SSTable文件的结构组成。
SSTable的特点
首先明确一下上文中提到的SSTable特点:
- 需要存储的格式的字节数据
- 键可以重复,键值对不需要对齐,即可以是任意长度的
- 需要支持高效的随机读取操作
读者们不妨想一想,如果让你设计一种数据结构来支持上述的设计目标,你会怎么设计呢?
SSTable文件的结构
一些辅助的类和结构体
在去看leveldb/table/table.cc文件内容时,经常会看到以下的几个类或者结构体,他们是对SSTable文件底层内容的抽象,它们使用起来像积木一样方便,能够帮助上层调用者关注底层的抽象,而且类型的名称取的也很到位,让代码阅读者很容易理解其用途。
BlockHandle:
BlockHandle的结构如下图所示:
BlockHandle的结构和其指向的文件数据
BlockHandle是指向文件中一部分连续数据的指针,顾名思义,可以指向一个数据块[data block]或者元信息块[meta block]。其实就是存储了两个成员变量:代表文件偏移的uint64_t offset_和代表数据长度的uint64_t size_。这样有了数据的起始地址和数据的长度,就可以定位到这片数据了。从其BlockHandle.Encode函数的实现可以知道总共需要占用16个字节就可以存储它序列化后的数据。
Slice:
Slice的结构如下图所示:
Slice和其指向的数据空间
Slice是一个简单的类,但是发挥的作用十分强大,可以用它来方便地指向一片连续存储的数据,可以认为指向的就是一个数组。它存储了指向的数据的起始地址和数据的字节长度。想想这样操作数据是不是就十分简洁了,再也不需要配套的两个变量来表示数据的起始地址和数据的大小了,使用 slice.data()拿到数组首地址,使用slice.size()拿到数组的长度,还有 slice.empty()来判断数组是否为空。需要注意的是这个类只是引用了一个数组,数组的存储空间由调用者管理,调用者必须保证在引用的数据被释放后,对应的slice对象不再使用。
BlockContents:
代表从文件中读取到的每一块[block]的数据的信息,包含实际的数据[Slice data],是否可以缓存[cachable]和是否是在堆上新申请的[heap_allocated]这三个属性。
SSTable存储到磁盘上的数据是支持压缩的,如果磁盘上的数据是经过压缩之后的,那就需要在从磁盘上读取的时候解压缩,此时BlockContents存储的是解压缩之后的数据,是在读取块数据时从堆上新申请的。heap_allocated就是用来标示这种情况,这片内存需要调用者负责最终释放。
SSTable文件的布局
从leveldb/doc/table_format.txt中可以看到,一个SSTable文件的内部数据的布局如下图所示:
SSTable文件的布局示意图
下文中提到的这些块数据是根据block_builder.cc中的代码来生成的。
- 数据块[data block]
文件中键值对key/value是有序存储的,被划分成一系列的数据块[data block]。这些块在文件的开头一块紧挨一块地存储起来。可以选择开启压缩选项来压缩数据之后再存储到数据块里。
可见数据块的存储方式就跟数组一样,是连续存储
- 元信息块[meta block]
在数据块之后,存储的是一些元信息块。每个元信息块数据也是由block_builder.cc中的代码生成的,也是支持压缩选项。可见元信息块的存储方式也跟数据块类似。
- 元索引数据块[metaindex block]
为每个元信息块生成一条记录,记录的key是元信息块的名称,记录的值是指向元信息块的BlockHandle。
可见每个元信息块中存储的数据用途不一样,而且一个块大小就完全能够存储的下。否则需要多个块,那记录的key就不能取元信息块的名称了。由于元信息有好几类,所以需要在这里存储给各个元信息块建立的索引。
- 索引块[index block]
这个块包含了为每个数据块生成的索引记录,key是一个>=这数据块中最后一个key并小于后续数据块首个key的字符串,value是指向数据块的BlockHandle。
可以看到上述的每种类型的块数据,都可以用一个BlockHandle来表示。
- Footer
每个文件尾部都包含一个固定长度[40 Bytes]的Footer,它包含了指向元索引块[metaindex block]和索引块[index block]的两个BlockHandle,以及一个8字节的magicNumber。
默认块的大小是大概是未压缩的4KB字节的数据。块的大小可以根据应用程序使用SSTable数据的方式来调整,例如主要是对数据库内容进行批量扫描的应用,可以适当调大块的大小;对于大量读取小数据的应用,可以调小块的大小来看看效率是否提升。
第一次接触这种类型的文件布局,可能很难理解上述的各个组成部分,可以尝试这样去理解:
- 由于机械磁盘本身是物理分块的[固态硬盘除外],所以SSTable也参照磁盘分块大小来进行数据分块,能够尽量减少访问磁盘的开销,而且即使有数据毁坏,也会局限在几个块上,不会影响其他块数据的读取。
- 为这些数据/元信息块建立索引,就类似给书籍制作目录页一样,便于读取某个key时,可以快速定位key落在哪个数据块上,而不需要顺序遍历SSTable的每个块。
- Footer相当于是最高一级的索引,从这里开始,就能够获取到SSTable所有的信息。因此把它设计成固定大小,这样从磁盘上反序列化SSTable时,首先把Footer读取出来,然后按图索骥,就可以把其他的块也加载到内存里。
上面提到了meta block,目前支持的meta block有以下两种:
- "过滤“元信息块["filter" Meta Block]
当数据库创建的时候指定了“过滤策略”,那么每个SSTable会存储一个过滤块[filter block][过滤策略所需的数据不超过一个块的大小]。“元索引”块包含一条记录,映射了从"filter."到指向过滤块的BlockHandle,这里""是过滤策略的"Name()"函数返回的字符串。
过滤块存储了一系列过滤器,其中filter i 包含了对一个文件偏移落于范围[ibase ... (i+1)base-1]上的数据块所存储的所有key执行FilterPolicy::CreateFilter()后的输出。
目前,“base”是2KB。所以举个例子,当块X和Y起始于范围[0KB .. 2KB-1],所有的X和Y的key都会通过调用FilterPolicy::CreateFilter()来被转换到同一个过滤器里,同时这个过滤器会被存储为过滤块里的第一个过滤器。
由于过滤串的长度不一,所以需要存储每个过滤器的起始地址。过滤块的格式见上图。
- “统计”元信息["stats" Meta Block]
这一块包含了一些统计信息。key是统计数据的名称。值是统计数字。
记录了如下统计信息:
数据大小
索引大小
键大小(未压缩)
值大小(未压缩)
记录的数量
数据块的数量
剩下还有一些细节没有介绍,例如索引块、数据块内部的格式,数据块的读取操作等,留待SSTable介绍系列的第三篇,也是最后一篇来介绍。
回到本系列目录:leveldb源码学习系列
SSTable 介绍(二)的更多相关文章
- Lucene.Net 2.3.1开发介绍 —— 二、分词(六)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(六) Lucene.Net的上一个版本是2.1,而在2.3.1版本中才引入了Next(Token)方法重载,而ReusableStrin ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(五)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(五) 2.1.3 二元分词 上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(三)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(三) 1.3 分词器结构 1.3.1 分词器整体结构 从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(四)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(四) 2.1.2 可以使用的内置分词 简单的分词方式并不能满足需求.前文说过Lucene.Net内置分词中StandardAnalyze ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(二)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(二) 1.2.分词的过程 1.2.1.分词器工作的过程 内置的分词器效果都不好,那怎么办?只能自己写了!在写之前当然是要先看看内置的分词 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(一)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...
- {Django基础十之Form和ModelForm组件}一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 ModelForm
Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Model ...
- MySQL之多表查询一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习
MySQL之多表查询 阅读目录 一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习 一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 首先说一下,我们写项目一般都会建 ...
- MySQL行(记录)的详细操作一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理
MySQL行(记录)的详细操作 阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操作: ...
随机推荐
- 使用phar上线你的代码包
在我前一阵子写的一篇文章<新版 SegmentFault 重构之系统架构>中,很多人对其中提到的利用phar上线代码比较感兴趣,我就在这边跟大家分享下我目前的做法. 哪些项目适合phar打 ...
- android图片的scaleType属性
scaleType 保持图片原有大小 scaleType="fitXY"填满盒子 scaleType="fitStart"保持纵横比缩放放在左上角 scaleT ...
- H5前端性能测试快速入门
前言 说到H5测试,对于做WEB测试的同学来说再熟悉不过了,它包括页H5功能测试,前端性能测试,浏览器兼容性能测试,以及服务端性能测试.那本文谈到的则是H5前端性能测试,并希望通过阅读本文后,能够知道 ...
- hd 2112 HDU Today
Problem Description 经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强.这时候,XHD ...
- Redis小结
一. Redis简介 Redis是一个速度非常快的高性能的key-value存储系统.redis的出现,很大程度补偿了memcached这类key/value存储的不足.Redis支持存储五种valu ...
- 使用CSS设置行间距,字间距.
字间距1.text-indent设置抬头距离css缩进即对,对应div设置css样式text-indent : 20px; 缩进了20px 2.letter-spacing来设置字与字间距_字符间距离 ...
- mac OS X 配置Python+Web.py+MySQLdb环境
MAC默认支持Python 2.7所以不用安装. 1.安装pip sudo easy_install pip 2.安装Web.py sudo pip install Web.py 3.安装MySQLd ...
- 【转】Linux中xargs的用法
xargs大 多数 Linux 命令都会产生输出:文件列表.字符串列表等.但如果要使用其他某个命令并将前一个命令的输出作为参数该怎么办?例如,file 命令显示文件类型(可执行文件.ascii 文本等 ...
- sqlserver的IO性能检查
这一个月老被一个信息科科长纠缠,原因就是他们的sql server 2008 R2老是定期的写入性能低下.我是这样认为的,但身边的人似乎都不这么想.每每我对那个挂在一个交换机上的网络存储表达担忧时,这 ...
- Access 2003下载地址和密钥
电驴下载链接:ed2k://|file|sc_access_2003.iso|645523456|63AA6C30D609FDC22D056C4B424283F9|/ 安装SN: HH74C-P8F7 ...