LevelDb实现原理

(by 刘延允)

引言

LevelDb本质上是一套存储系统以及在这套存储系统上提供的一些操作接口。为了便于理解LevelDB的存储结构以及工作流程,我们可以从两个不同的角度来分析一下LevleDb的原理:静态角度和动态角度。静态角度:主要了解系统如何对数据进行存储的,其数据是如何组织管理的;动态角度,主要是了解系统是如何对数据进行操作的,其操作流程是怎么回事,系统是如何运转起来的。

LevelDb整体架构及其存储结构

LevelDb实现原理_第1张图片
上图展示的是LevelDb框架图(3Level),从图中可以看出,LevelDb系统结构主要包括以下部分:

  • 内存中的MemTable
  • 内存中的Immutable MemTable
  • 磁盘上的Log文件
  • 磁盘上的Manifest文件
  • 磁盘上的Current文件
  • 磁盘上的SSTable文件
    当然还有一些其他辅助文件,以及为了加快读取放在内存中Cache缓存
  1. MemTable是一个跳跃表为了便于快速查找,Immutable MemTable是一个只读MemTable,结构完全一样。可以认为它俩是一个双缓冲的典型用法,当MemTable满了的时候就变为Immutable MemTable,然后建立一个新的MemTable,这样原先的MemTable就可以在后台用于干其他事情了。
  2. Log文件用于顺序记录写入的Key-Value记录,主要用于系统崩溃时恢复数据使用,不至于大批量丢失数据(依赖于fsync)
  3. Manifest文件用于记录每个sst文件中key的分布情况,按照key排序后进行分段索引,在sst文件进行合并时会更新
  4. Current文件用于指示当前正在使用的Manifest,Current文件的内容就是正在使用Manifest文件名
  5. SST文件是数据文件,文件中有两部分数据:Key-Value数据、索引数据用于快速查找key

在物理磁盘上的组织结构如下图所示(7Level):
LevelDb实现原理_第2张图片

文件夹sst_0 … sst_6分别是7级level数据的存放文件夹,里面每个文件是一个数据文件,每一级比上一级的存储容量增大10倍,当超出容量时会触发Compaction操作,与下一级进行合并并存储到下一级中,腾出本级的存储空间。

LevelDb的数据写入流程

在LevelDb中本质上只存在写入,并不存在所谓的更新与删除机制;更新与删除操作是通过写入变相来实现的。我们通过下面的操作例子进行说明:
1. Add key:张三
2. Update key:李四
3. Update key:王五
上面三个操作先增加一个key,然后更新两次,最终的值应该是‘王五’;在获取的时候按照操作流水优先查找最新的操作,可以获取到最新值是‘王五’。
同理删除的时候在操作流水中标记此key已经删除,这样在获取的时候得知key已经被删也就不存在了。也就是说LevelDb以最后的一次操作为最终值。
理解了上面的例子就基本算是理解了LevelDb的工作原理,LevelDb干的活就是对上面流程进行优化再优化。

LevelDb实现原理_第3张图片
从上图可以看出LevelDb的一次更新操作有两个步骤:顺序写log文件、更新Memtable;Leveldb的写入就是如此的简单,所以说他的写入是非常高效的,真正麻烦的是读取操作;这也就是别人常说的LevelDb适用于写多读少的场景。

其实一次写入还是会触发很多一系列后期操作的,文件存储的组织结构都是由这一次写入触发的。

  • 一个MemTable对应一个log文件;当MemTable满了以后,会变为Immutable MemTable,然后创建一个新的MemTable与log文件
  • Immutable MemTable中的数据数据会被dump到level0的目录文件中
  • 如果level0的容量超过定值会与level1中的数据进行合并存储到level1中,以此往下类推。。。
    通过上面的写入流程我们可以看出数据的新旧情况:
    MemTable > Immutable MemTable > level0 > level1 > level2 …
    上面对数据新旧情况的定论是下面将要讨论的读取流程的理论基础

LevelDb的数据读取流程

![这里写图片描述](https://img-blog.csdn.net/20170502144208724?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2R1dGxpdXl1bjgyNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) LevelDb的读取流程如上图所示;按照上面的结论,优先读取最新的结构,读到则返回,查找到最后还是找不到说明key不存在,所以说经常读取不存在的数据是最耗资源的,读取越懒惰的数据越慢,读取越新的数据越快。

在MemTable、Immutable MemTable查找时就是对跳跃表的查找,内存操作比较快。
在SST文件中查找时,先从Manifest文件中查找是否有符合条件的分段索引,符合条件的有可能在好几个level文件夹中都有,甚至每个文件夹中都有,优先从最新的level中进行查找;在sst文件中查找时先在sst的索引数据中找到数据存储位置,然后读出真实数据

LevelDb中的Cache

Cache是为了快速读取准备,主要有两个:TableCache、BlockCache
  • TableCache:以sst文件名为key缓存打开的文件句柄、以及sst文件中索引信息等
  • BlockCache: 是记录的sst文件中cacheid与内容的缓存对
    正是由于有了这层Cache所以常说LevelDb的顺序读性能还不错,顺序读指的是按照key大小排序。

你可能感兴趣的:(数据库,存储系统,Leveldb)