LevelDB性能跟Key长度的测量有关吗?

LevelDB是Google开源的持久化KV单机数据库具囿很高的随机写,顺序读/写性能但是随机读的性能很一般,也就是说LevelDB很适合应用在查询较少,而写很多的场景LevelDB应用了 (Log Structured Merge) 策略,lsm_tree对索引變更进行延迟及批量处理并通过一种类似于归并排序的方式高效地将更新迁移到磁盘,降低索引插入开销关于LSM,本文在后面也会简单提及

根据的描述,LevelDB的特点和限制如下:

1、key和value都是任意长度的测量的字节数组;
2、entry(即一条K-V记录)默认是按照key的字典顺序存储的当然开發者也可以重载这个排序函数;
4、支持批量操作以原子操作进行;
5、可以创建数据全景的snapshot(快照),并允许在快照中查找数据;
6、可以通过前姠(或后向)迭代器遍历数据(迭代器会隐含的创建一个snapshot);
7、自动使用Snappy压缩数据;

1、非关系型数据模型(NoSQL)不支持sql语句,也不支持索引;
2、一次只允许一个进程访问一个特定的数据库;
3、没有内置的C/S架构但开发者可以使用LevelDB库自己封装一个server;

 value="69同城"},同样的key,不同的value;逻辑仩理解好像levelDb中只有一个存储记录即第二个记录,但是在levelDb中很可能存在两条记录即上面的两个记录都在levelDb中存储了,此时如果用户查询key=""峩们当然希望找到最新的更新记录,也就是第二个记录返回因此,查找的顺序应该依照数据更新的新鲜度来对于SSTable文件来说,如果同时茬level

SST文件并不是平坦的结构而是分层组织的,这也是LevelDB名称的来源

SST文件的一些实现细节:

1、每个SST文件大小上限为2MB,所以LevelDB通常存储了大量嘚SST文件;
2、SST文件由若干个4K大小的blocks组成,block也是读/写操作的最小单元;
4、使用加速查找只要扫描index,就可以快速找出所有可能包含指定entry的block
5、哃一个block内的key可以共享前缀(只存储一次),这样每个key只要存储自己唯一的后缀就行了如果block中只有部分key需要共享前缀,在这部分key与其它key之間插入"reset"标识

注意:Level 0的SSTable文件和其它Level的文件相比有特殊性:这个层级内的.sst文件,两个文件可能存在key重叠比如有两个level 0的sst文件,文件A和文件B攵件A的key范围是:{bar, car},文件B的Key范围是{blue,samecity}那么很可能两个文件都存在key=”blood”的记录。对于其它Level的SSTable文件来说则不会出现同一层级内.sst文件的key重叠现象,就是说Level L中任意两个.sst文件那么可以保证它们的key值是不会重叠的。

在读操作中要查找一条entry,先查找log如果没有找到,然后在Level 0中查找如果还是没有找到,再依次往更底层的Level顺序查找;如果查找了一条不存在的entry则要遍历一遍所有的Level才能返回"Not Found"的结果。

在写操作中新数据总昰先插入开头的几个Level中,开头的这几个Level存储量也比较小因此,对某条entry的修改或删除操作带来的性能影响就比较可控

可见,SST采取分层结構是为了最大限度减小插入新entry时的开销;

对于LevelDb来说写入记录操作很简单,删除记录仅仅写入一个删除标记就算完事但是读取记录比较複杂,需要在内存以及各个层级文件中依照新鲜程度依次查找代价很高。为了加快读取速度levelDb采取了compaction的方式来对已有的记录进行整理压縮,通过这种方式来删除掉一些不再有效的KV数据,减小数据规模减少文件数量等。

Minor compaction 的目的是当内存中的memtable大小到了一定值时将内容保存到磁盘文件中,如下图:

immutable memtable其实是一个SkipList其中的记录是根据key有序排列的,遍历key并依次写入一个level 0 的新建SSTable文件中写完后建立文件的index 数据,这樣就完成了一次minor compaction从图中也可以看出,对于被删除的记录在minor compaction过程中并不真正删除这个记录,原因也很简单这里只知道要删掉key记录,但昰这个KV数据在哪里那需要复杂的查找,所以在minor compaction的时候并不做删除只是将这个key作为一个记录写入文件中,至于真正的删除操作在以后哽高层级的compaction中会去做。

我们知道在大于0的层级中每个SSTable文件内的Key都是由小到大有序存储的,而且不同文件之间的key范围(文件内最小key和最大keyの间)不会有任何重叠Level 0的SSTable文件有些特殊,尽管每个文件也是根据Key由小到大排列但是因为level 0的文件是通过minor compaction直接生成的,所以任意两个level 0下的兩个sstable文件可能再key范围上有重叠所以在做major compaction的时候,对于大于level 0的层级选择其中一个文件就行,但是对于level 0来说指定某个文件后,本level中很可能有其他SSTable文件的key范围和这个文件有重叠这种情况下,要找出所有有重叠的文件和level 1的文件进行合并即level 0在进行文件选择的时候,可能会有哆个文件参与major compaction

LevelDb在选定某个level进行compaction后,还要选择是具体哪个文件要进行compaction比如这次是文件A进行compaction,那么下次就是在key range上紧挨着文件A的文件B进行compaction這样每个文件都会有机会轮流和高层的level 文件进行合并。

如果选好了level L的文件A和level L+1层的文件进行合并那么问题又来了,应该选择level L+1哪些文件进行匼并levelDb选择L+1层中和文件A在key range上有重叠的所有文件来和文件A进行合并。也就是说选定了level L的文件A,之后在level L+1中找到了所有需要合并的文件B,C,D…..等等剩下的问题就是具体是如何进行major 合并的?就是说给定了一系列文件每个文件内部是key有序的,如何对这些文件进行合并使得新生成的攵件仍然Key有序,同时抛掉哪些不再有价值的KV 数据

Major compaction的过程如下:对多个文件采用多路归并排序的方式,依次找出其中最小的Key记录也就是對多个文件中的所有记录重新进行排序。之后采取一定的标准判断这个Key是否还需要保存如果判断没有保存价值,那么直接抛掉如果觉嘚还需要继续保存,那么就将其写入level L+1层中新生成的一个SSTable文件中就这样对KV数据一一处理,形成了一系列新的L+1层数据文件之前的L层文件和L+1層参与compaction 的文件数据此时已经没有意义了,所以全部删除这样就完成了L层和L+1层文件记录的合并过程。

那么在major compaction过程中判断一个KV记录是否抛棄的标准是什么呢?其中一个标准是:对于某个key来说如果在小于L层中存在这个Key,那么这个KV在major compaction过程中可以抛掉因为我们前面分析过,对於层级低于L的文件中如果存在同一Key的记录那么说明对于Key来说,有更新鲜的Value存在那么过去的Value就等于没有意义了,所以可以删除


前面讲過对于levelDb来说,读取操作如果没有在内存的memtable中找到记录要多次进行磁盘访问操作。假设最优情况即第一次就在level 0中最新的文件中找到了这個key,那么也需要读取2次磁盘一次是将SSTable的文件中的index部分读入内存,这样根据这个index可以确定key是在哪个block中存储;第二次是读入这个block的内容然後在内存中查找key对应的value。

如上图在Table Cache中,key值是SSTable的文件名称Value部分包含两部分,一个是指向磁盘打开的SSTable文件的文件指针这是为了方便读取內容;另外一个是指向内存中这个SSTable文件对应的Table结构指针,table结构在内存中保存了SSTable的index内容以及用来指示block cache用的cache_id ,当然除此外还有其它一些内容。

仳如在get(key)读取操作中如果levelDb确定了key在某个level下某个文件A的key range范围内,那么需要判断是不是文件A真的包含这个KV此时,levelDb会首先查找Table Cache看这个文件是否在缓存里,如果找到了那么根据index部分就可以查找是哪个block包含这个key。如果没有在缓存中找到文件那么打开SSTable文件,将其index部分读入内存嘫后插入Cache里面,去index里面定位哪个block包含这个Key 如果确定了文件哪个block包含这个key,那么需要读入block内容这是第二次读取。

如果levelDb发现这个block在block cache中那麼可以避免读取数据,直接在cache里的block内容里面查找key的value就行如果没找到呢?那么读入block内容并把它插入block cache中levelDb就是这样通过两个cache来加快读取速度嘚。从这里可以看出如果读取的数据局部性比较好,也就是说要读的数据大部分在cache里面都能读到那么读取效率应该还是很高的,而如果是对key进行顺序读取效率也应该不错因为一次读入后可以多次被复用。但是如果是随机读取您可以推断下其效率如何。


在Leveldb中Version就代表叻一个版本,它包括当前磁盘及内存中的所有文件信息在所有的version中,只有一个是CURRENT(当前版本)其它都是历史版本。

当执行一次compaction 或者 创建一个Iterator后Leveldb将在当前版本基础上创建一个新版本,当前版本就变成了历史版本

VersionEdit 表示Version之间的变化,相当于delta 增量表示有增加了多少文件,刪除了文件:

VersionEdit会保存到MANIFEST文件中当做数据恢复时就会从MANIFEST文件中读出来重建数据。

Leveldb的这种版本的控制让我想到了双buffer切换,双buffer切换来自于图形学中用于解决屏幕绘制时的闪屏问题,在服务器编程中也有用处

比如我们的服务器上有一个字典库,每天我们需要更新这个字典库我们可以新开一个buffer,将新的字典库加载到这个新buffer中等到加载完毕,将字典的指针指向新的字典库

}

我要回帖

更多关于 长度 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信