LevelDB深入浅出之整体架构
LevelDB是一个可持久化的KV数据库引擎,由Google传奇工程师Jeff Dean和Sanjay Ghemawat开发并开源。无论从设计还是代码上都可以用精致优雅来形容,非常值得细细品味。本文将从整体特性、架构和使用等几方面做一个解释,试图通过本文的介绍让大家对LevelDB有个整体的认识并能够使用。
设计思路
做存储的同学都很清楚,对于普通机械磁盘顺序写的性能要比随机写大很多。比如对于15000转的SAS盘,4K写IO, 顺序写在200MB/s左右,而随机写性能可能只有1MB/s左右。而LevelDB的设计思想正是利用了磁盘的这个特性。
LevelDB的数据是存储在磁盘上的,采用LSM-Tree的结构实现。LSM-Tree将磁盘的随机写转化为顺序写,从而大大提高了写速度。为了做到这一点LSM-Tree的思路是将索引树结构拆成一大一小两颗树,较小的一个常驻内存,较大的一个持久化到磁盘,他们共同维护一个有序的key空间。写入操作会首先操作内存中的树,随着内存中树的不断变大,会触发与磁盘中树的归并操作,而归并操作本身仅有顺序写。如下图所示:
图1 数据存储原理图中2个红色区域是要进行归并的数据块,计算出顺序后会存储到如图下面的磁盘空间,而这种存储方式是追加式的,也就是顺序写入磁盘。
随着数据的不断写入,磁盘中的树会不断膨胀,为了避免每次参与归并操作的数据量过大,以及优化读操作的考虑,LevelDB将磁盘中的数据又拆分成多层,每一层的数据达到一定容量后会触发向下一层的归并操作,每一层的数据量比其上一层成倍增长。这也就是LevelDB的名称来源。主要特性
下面是LevelDB官方对其特性的描述,主要包括如下几点:
key和value都是任意长度的字节数组;entry(即一条K-V记录)默认是按照key的字典顺序存储的,当然开发者也可以重载这个排序函数;提供的基本操作接口:Put()、Delete()、Get()、Batch();支持批量操作以原子操作进行;可以创建数据全景的snapshot(快照),并允许在快照中查找数据;可以通过前向(或后向)迭代器遍历数据(迭代器会隐含的创建一个snapshot);自动使用Snappy压缩数据;可移植性;编译和使用
LevelDB的编译也是比较简单的,可以从官网直接克隆代码(请自己找下载地址,sx百度提示广告)。具体操作步骤如下(可以参考源代码中的README文件):

完成上述几步,就可以编译出一个静态库、一个动态库和一些测试程序。我们可以自己写一个测试代码进行测试。比如我们在leveldb目录下面创建一个test目录, 然后将静态库libleveldb.a拷贝进来,然后在其中创建一个名为test.cpp的文件,文件内容如下:

测试程序功能很简单,就是向KV数据库添加1000个KV数据。后续我们还会用到这个程序,这里了解一下就行。具体通过如下命令可以编译成可执行程序。
g++ -o leveldbTest test.cpp libleveldb.a -lpthread -lsnappy
如果运行一下该程序,可以看到在当前目录会生成一个名为testbd1的目录,其中的内容如下所示:
图2 LevelDB的主要文件整体结构
对LevelDB有一个整体的认识之后,我们分析一下它的架构。这里面有一个重要的概念(或者模块)需要理解,分别是内存数据的Memtable,分层数据存储的SST文件,版本控制的Manifest、Current文件,以及写Memtable前的WAL。这里简单介绍各个组件的作用和在整个结构中的位置,更详细的介绍本号将另外写文章进行介绍。在介绍之前,我们先看一下整体架构示意图:
图3 LevelDB整体架构图如图3所示,对于写数据,接口会同时写入内存表(MemTable)和日志中。当内存表达到阈值时,内存表冻结,变为Immutable MemTable,并将数据写入SST表中,其中SST表时在磁盘上的文件。下面是涉及到主要模块的简单介绍:
Memtable:内存数据结构,跳表实现,新的数据会首先写入这里;Log文件:写Memtable前会先写Log文件,Log通过append的方式顺序写入。Log的存在使得机器宕机导致的内存数据丢失得以恢复;Immutable Memtable:达到Memtable设置的容量上限后,Memtable会变为Immutable为之后向SST文件的归并做准备,顾名思义,Immutable Mumtable不再接受用户写入,同时会有新的Memtable生成;SST文件:磁盘数据存储文件。分为Level 0到Level N多层,每一层包含多个SST文件;单层SST文件总量随层次增加成倍增长;文件内数据有序;其中Level0的SST文件由Immutable直接Dump产生,其他Level的SST文件由其上一层的文件和本层文件归并产生;SST文件在归并过程中顺序写生成,生成后仅可能在之后的归并中被删除,而不会有任何的修改操作。Manifest文件: Manifest文件中记录SST文件在不同Level的分布,单个SST文件的最大最小key,以及其他一些LevelDB需要的元信息。Current文件: 从上面的介绍可以看出,LevelDB启动时的首要任务就是找到当前的Manifest,而Manifest可能有多个。Current文件简单的记录了当前Manifest的文件名,从而让这个过程变得非常简单。写流程简析
了解了整体流程和架构后,我们分析两个基本的流程,也就是LevelDB的写流程和读流程。我们这里首先分析一下写流程,毕竟要先有数据后才能读数据。
LevelDB的写操作包括设置key-value和删除key两种。需要指出的是这两种情况在LevelDB的处理上是一致的,删除操作其实是向LevelDB插入一条标识为删除的数据。下面我们先看一下LevelDB插入值的整体流程,具体如图4所示。
图4 LevelDB写数据流程具体代码请自行阅读,本文不贴过多的代码。这里需要重点说明的是DBImpl::Write函数,如图5是该函数的代码片段,从这段代码中我们可以很清楚的看到数据被分别写入日志和内存2个地方。其它代码都比较简单,大家请自行对照流程图阅读。
图5 DBImpl::Write代码片段读流程简析
读流程要比写流程简单一些,核心代码逻辑如图6所示。首先,生成内部查询所用的Key,该Key是由用户请求的UserKey拼接上Sequence生成的。其中Sequence可以用户提供或使用当前最新的Sequence,LevelDB可以保证仅查询在这个Sequence之前的写入。然后,用生成的Key,依次尝试从 Memtable,Immtable以及SST文件中读取,直到找到。
图6 LevelDB读流程从SST文件中查找需要依次尝试在每一层中读取,得益于Manifest中记录的每个文件的key区间,我们可以很方便的知道某个key是否在文件中。Level0的文件由于直接由Immutable Dump 产生,不可避免的会相互重叠,所以需要对每个文件依次查找。对于其他层次,由于归并过程保证了其互相不重叠且有序,二分查找的方式提供了更好的查询效率。可以看出同一个Key出现在上层的操作会屏蔽下层的。也因此删除Key时只需要在Memtable压入一条标记为删除的条目即可。被其屏蔽的所有条目会在之后的归并过程中清除。好了,今天就先到这,后续我们在深入的介绍LevelDB的其它部分的,并且逐步深入,理解其内部的精髓。
LevelDB深入浅出之整体架构的更多相关文章
- LevelDB系列之整体架构
LevelDb本质上是一套存储系统以及在这套存储系统上提供的一些操作接口.为了便于理解整个系统及其处理流程,我们可以从两个不同的角度来看待LevleDb:静态角度和动态角度.从静态角度,可以假想整个系 ...
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析--整体架构(转)
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- LevelDB 整体架构
[LevelDB 整体架构] 从图中可以看出,构成LevelDb静态结构的包括六个主要部分:内存中的MemTable和Immutable MemTable以及磁盘上的几种主要文件:Curren ...
- jQuery整体架构源码解析(转载)
jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...
- jQuery整体架构源码解析
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 蓝牙 BLE 协议学习: 001-BLE协议栈整体架构
背景 在深入BLE协议帧之前,我们先看一下BLE协议栈整体架构. 转载自:<深入浅出低功耗蓝牙(BLE)协议栈> 架构 如上图所述,要实现一个BLE应用,首先需要一个支持BLE射频的芯片, ...
- 大家都能看得懂的源码(一)ahooks 整体架构篇
本文是深入浅出 ahooks 源码系列文章的第一篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 第一篇主要介绍 ahooks 的背景以及整体架构. React h ...
- Underscore 整体架构浅析
前言 终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度.十一月,多事之秋,最近好多事情搞的 ...
随机推荐
- 纪念一下,时隔多年,继delphi上成功运行sql之后
纪念一下,时隔多年,继delphi上成功运行sql之后,在linux上用eclipse成功运行Wordcount程序,不容易呀,折腾了十几天 但我知道,这仅仅是我学习大数据的一个开始,仅此纪念一下,不 ...
- c语言二维数组赋值
, n=;//行数和列数 pattern = (char**)malloc(sizeof(char*)*m);//申请一组一维指针空间. ; i<m; i++) pattern[i] = (ch ...
- Linux教程 Yum命令的使用
在这篇文章中,我们将学习如何安装,更新,删除,查找安装包,管理安装包以及安装包的仓库在Linux系统使用RedHat开发的YUM(Yellowdog Updater Modified)工具.以下这些命 ...
- Centos杀死进程kill方法大全
杀死进程最安全的方法是单纯使用kill命令. 首先使用ps -ef命令确定要杀死进程的PID,然后输入以下命令: # kill -pid 注释:标准的kill命令通常都能达到目的.终止有问题的进程,并 ...
- Java字节码方法表与属性表深度剖析
方法表: 在上一次咱们已经分析到了字段信息了,如下: 紧接着就是方法相关的信息了: 而它展开之后的结构为: 所以往后数2个字节,看一下方法的总数: 3个方法,可咱们只定义了两个方法呀: 因为编译器会为 ...
- hbase实践之Rowkey设计之道
笔者从一开始接触hbase就在思考rowkey设计,希望rowkey设计得好,能够支持查询的需求.使用hbase一段时间后,再去总结一些hbase的设计方法,无外乎以下几种: reverse salt ...
- 2018web前端面试题总结
web面试题 css面试 一.css盒模型 css中的盒子模型包括IE盒子模型和标准的W3C盒子模型.border-sizing: border-box, inherit, content-box ...
- 「数据结构与算法(Python)」(一)
算法的提出 算法的概念 算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务.一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写 ...
- nginx的access log按小时生成
1.在server或location段进行配置 if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})&q ...
- The method setCharacterEncoding(String) is undefined for the type HttpServletResponse
今天将以前做的一个web项目从不笔记本上移到台式机上,import项目后出现“The method setCharacterEncoding(String) is undefined for the ...