现在的位置: 首页 > 综合 > 正文

Leveldb 实现原理

2014年01月26日 ⁄ 综合 ⁄ 共 5202字 ⁄ 字号 评论关闭

转自:http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html、http://www.samecity.com/blog/Index.asp?SortID=12

说起LevelDb也许您不清楚但是如果作为IT工程师不知道下面两位大神级别的工程师那您的领导估计会Hold不住了Jeff
DeanSanjay Ghemawat。这两位是Google公司重量级的工程师为数甚少的Google
Fellow之二

  Jeff Dean其人http://research.google.com/people/jeff/index.html,Google大规模分布式平台BigtableMapReduce主要设计和实现者

  Sanjay Ghemawat其人http://research.google.com/people/sanjay/index.html,Google大规模分布式平台GFS,BigtableMapReduce主要设计和实现工程师

  LevelDb就是这两位大神级别的工程师发起的开源项目简而言之LevelDb是能够处理十亿级别规模Key-Value型数据持久性存储的C++ 程序库正像上面介绍的这二位是Bigtable的设计和实现者如果了解Bigtable的话应该知道在这个影响深远的分布式存储系统中有两个核心的部分Master
ServerTablet Server。其中Master Server做一些管理数据的存储以及分布式调度工作实际的分布式数据存储以及读写操作是由Tablet
Server完成的LevelDb则可以理解为一个简化版的Tablet Server。

  LevelDb有如下一些特点

    首先LevelDb是一个持久化存储的KV系统Redis这种内存型的KV系统不同LevelDb不会像Redis一样狂吃内存而是将大部分数据存储到磁盘上

    其次LevleDb在存储数据时是根据记录的key值有序存储的就是说相邻的key值在存储文件中是依次顺序存储的而应用可以自定义key大小比较函数LevleDb会按照用户定义的比较函数依序存储这些记录

    再次像大多数KV系统一样LevelDb的操作接口很简单基本操作包括写记录读记录以及删除记录也支持针对多条操作的原子批量操作

    另外LevelDb支持数据快照snapshot)功能使得读取操作不受写操作影响可以在读操作过程中始终看到一致的数据

  除此外LevelDb还支持数据压缩等操作这对于减小存储空间以及增快IO效率都有直接的帮助

  LevelDb性能非常突出官方网站报道其随机写性能达到40万条记录每秒而随机读性能达到6万条记录每秒总体来说LevelDb的写操作要大大快于读操作而顺序读写操作则大大快于随机读写操作至于为何是这样看了我们后续推出的LevelDb日知录估计您会了解其内在原因

LevelDb日知录之二:整体架构

      LevelDb本质上是一套存储系统以及在这套存储系统上提供的一些操作接口为了便于理解整个系统及其处理流程我们可以从两个不同的角度来看待LevleDb:静态角度和动态角度从静态角度可以假想整个系统正在运行过程中不断插入删除读取数据),此时我们给LevelDb照相从照片可以看到之前系统的数据在内存和磁盘中是如何分布的处于什么状态等从动态的角度主要是了解系统是如何写入一条记录读出一条记录删除一条记录的同时也包括除了这些接口操作外的内部操作比如compaction,系统运行时崩溃后如何恢复系统等等方面

     本节所讲的整体架构主要从静态角度来描述之后接下来的几节内容会详述静态结构涉及到的文件或者内存数据结构LevelDb日知录后半部分主要介绍动态视角下的LevelDb,就是说整个系统是怎么运转起来的

     LevelDb作为存储系统数据记录的存储介质包括内存以及磁盘文件如果像上面说的LevelDb运行了一段时间此时我们给LevelDb进行透视拍照那么您会看到如下一番景象

1.1:LevelDb结构

    从图中可以看出构成LevelDb静态结构的包括六个主要部分内存中的MemTableImmutable
MemTable以及磁盘上的几种主要文件Current文件Manifest文件log文件以及SSTable文件当然LevelDb除了这六个主要部分还有一些辅助的文件但是以上六个文件和数据结构是LevelDb的主体构成元素

LevelDbLog文件和MemtableBigtable论文中介绍的是一致的当应用写入一条Key:Value记录的时候LevelDb会先往log文件里写入成功后将记录插进Memtable这样基本就算完成了写入操作因为一次写入操作只涉及一次磁盘顺序写和一次内存写入所以这是为何说LevelDb写入速度极快的主要原因

Log文件在系统中的作用主要是用于系统崩溃恢复而不丢失数据假如没有Log文件因为写入的记录刚开始是保存在内存中的此时如果系统崩溃内存中的数据还没有来得及Dump到磁盘所以会丢失数据Redis就存在这个问题)。为了避免这种情况LevelDb在写入内存前先将操作记录到Log文件中然后再记入内存中这样即使系统崩溃也可以从Log文件中恢复内存中的Memtable,不会造成数据的丢失

Memtable插入的数据占用内存到了一个界限后需要将内存的记录导出到外存文件中LevleDb会生成新的Log文件和Memtable,原先的Memtable就成为Immutable
Memtable,顾名思义就是说这个Memtable的内容是不可更改的只能读不能写入或者删除新到来的数据被记入新的Log文件和Memtable,LevelDb后台调度会将Immutable
Memtable的数据导出到磁盘形成一个新的SSTable文件SSTable就是由内存中的数据不断导出并进行Compaction操作后形成的而且SSTable的所有文件是一种层级结构第一层为Level
0,第二层为Level 1,依次类推层级逐渐增高这也是为何称之为LevelDb的原因

    SSTable中的文件是Key有序的就是说在文件中小key记录排在大Key记录之前各个LevelSSTable都是如此但是这里需要注意的一点是Level
0SSTable文件后缀为.sst)和其它Level的文件相比有特殊性这个层级内的.sst文件两个文件可能存在key重叠比如有两个level
0sst文件文件A和文件B,文件Akey范围是:{bar,
car},文件BKey范围是{blue,samecity},那么很可能两个文件都存在key=”blood”的记录对于其它LevelSSTable文件来说则不会出现同一层级内.sst文件的key重叠现象就是说Level
L中任意两个.sst文件那么可以保证它们的key值是不会重叠的这点需要特别注意后面您会看到很多操作的差异都是由于这个原因造成的

    SSTable中的某个文件属于特定层级而且其存储的记录是key有序的那么必然有文件中的最小key和最大key,这是非常重要的信息LevelDb应该记下这些信息Manifest就是干这个的它记载了SSTable各个文件的管理信息比如属于哪个Level,文件名称叫啥最小key和最大key各自是多少下图是Manifest所存储内容的示意

2.1:Manifest存储示意图

图中只显示了两个文件manifest会记载所有SSTable文件的这些信息),Level
0test.sst1test.sst2文件同时记载了这些文件各自对应的key范围比如test.sstt1key范围是an” “banana”,而文件test.sst2key范围是baby”samecity”,可以看出两者的key范围是有重叠的

Current文件是干什么的呢这个文件的内容只有一个信息就是记载当前的manifest文件名因为在LevleDb的运行过程中随着Compaction的进行SSTable文件会发生变化会有新的文件产生老的文件被废弃Manifest也会跟着反映这种变化此时往往会新生成Manifest文件来记载这种变化Current则用来指出哪个Manifest文件才是我们关心的那个Manifest文件

以上介绍的内容就构成了LevelDb的整体静态结构LevelDb日知录接下来的内容中我们会首先介绍重要文件或者内存数据的具体数据布局与结构

LevelDb日知录之三:log文件

     上节内容讲到log文件在LevelDb中的主要作用是系统故障恢复时能够保证不会丢失数据因为在将记录写入内存的Memtable之前会先写入Log文件这样即使系统发生故障Memtable中的数据没有来得及Dump到磁盘的SSTable文件LevelDB也可以根据log文件恢复内存的Memtable数据结构内容不会造成系统丢失数据在这点上LevelDbBigtable是一致的

     下面我们带大家看看log文件的具体物理和逻辑布局是怎样的LevelDb对于一个log文件会把它切割成以32K为单位的物理Block,每次读取的单位以一个Block作为基本读取单位下图展示的log文件由3Block构成所以从物理布局来讲一个log文件就是由连续的32K大小Block构成的

3.1 log文件布局

        在应用的视野里是看不到这些Block应用看到的是一系列的Key:ValueLevelDb内部会将一个Key:Value对看做一条记录的数据另外在这个数据前增加一个记录头用来记载一些管理信息以方便内部处理3.2显示了一个记录在LevelDb内部是如何表示的

 

3.2 记录结构

       记录头包含三个字段ChechSum是对类型数据字段的校验码为了避免处理不完整或者是被破坏的数据LevelDb读取记录数据时候会对数据进行校验如果发现和存储的CheckSum相同说明数据完整无破坏可以继续后续流程。“记录长度记载了数据的大小,“数据则是上面讲的Key:Value数值对,“类型字段则指出了每条记录的逻辑结构和log文件物理分块结构之间的关系具体而言主要有以下四种类型FULL/FIRST/MIDDLE/LAST。

        如果记录类型是FULL,代表了当前记录内容完整地存储在一个物理Block没有被不同的物理Block切割开如果记录被相邻的物理Block切割开则类型会是其他三种类型中的一种我们以图3.1所示的例子来具体说明

       假设目前存在三条记录Record A,Record BRecord C,其中Record
A大小为10K,Record B 大小为80K,Record
C大小为12K,那么其在log文件中的逻辑布局会如图3.1所示Record
A是图中蓝色区域所示因为大小为10K<32K,能够放在一个物理Block所以其类型为FULL;Record
大小为80K,Block 1因为放入了Record
A,所以还剩下22K,不足以放下Record B,所以在Block
1的剩余部分放入Record B的开头一部分类型标识为FIRST,代表了是一个记录的起始部分Record
B还有58K没有存储这些只能依次放在后续的物理Block里面因为Block
2大小只有32K,仍然放不下Record B的剩余部分所以Block
2全部用来放Record B,且标识类型为MIDDLE,意思是这是Record B中间一段数据Record
B剩下的部分可以完全放在Block 3类型标识为LAST,代表了这是Record
B的末尾数据图中黄色的Record C因为大小为12K,Block
3剩下的空间足以全部放下它所以其类型标识为FULL。

     从这个小例子可以看出逻辑记录和物理Block之间的关系LevelDb一次物理读取为一个Block,然后根据类型情况拼接出逻辑记录供后续流程处理

LevelDb日知录之四:SSTable文件

   SSTableBigtable中至关重要的一块

抱歉!评论已关闭.