leveldb是一个写性能十分优秀的存储引擎,是典型的LSM-tree的实现。LSM的核心思想是为了换取最大的写性能而放弃掉部分读性能。那么,为什么leveldb写性能高?简单来说它就是尽量减少随机写的次数。leveldb首先将数据更新到内存中。当内存中的数据量达到一定阈值,将这部分数据再真正刷新到磁盘文件中。一般来说,顺序写60MB/s,随机写45MB/s.
leveldb主要由以下几个重要的部件构成:
1.memtable
2.immutable memtable
3.sstable
4.manifest
5.current
6.log
刚才提到leveldb的一次写入操作并不是直接将数据写入到磁盘文件,而是采用先将数据写入内存的方式。所以,memtable就是一个内存中进行数据组织与维护的结构。在memtable中,数据按用户定义的方法排序之后按序存储。等到其存储内容到达阈值时(4MB)时,便将其转换成一个不可修改的memtable,与此同时创建一个新的memtable来供用户进行读写操作。memtable底层采用跳表,它的大多数操作都是O(logn)。
当memtable的容量达到阈值时,便会转换成一个不可修改的memtable即immutable memtable。它同memtable的结构定义一样。两者的区别只是immutable memtable是只读的。immutable memtable被创建时,leveldb的后台压缩进程便会利用其中的内容创建一个sstable,然后持久化到磁盘中。
在leveldb中有个版本的概念。一个版本记录了每一层所有文件的元数据。元数据包括如下几点:
- 文件大小
- 最大key值
- 自小key值
版本信息十分关键,除了在查找数据时利用两个key值来加快查找,还在其中为了一些compaction的统计值来控制compaction的进行。
可以看到文件的元数据主要包含最小和最小key
版本则维护了每一层所有文件的元数据信息。入下代码所示:
当每次compaction完成时,leveldb都会创建一个新的version。compaction完成简单来说就是sstable的新增或者减少。而version创建的规则是:
versionNew = versionOld + versionEdit
这里的versionEdit指的是在旧版本基础上变化的内容。一般指sstable的增加或者删除。
manifest文件就是用来记录这些versionEdit信息的。一个versionEdit数据会编码成一条记录写入到manifest文件中。如下图所示
一共有两条versionEdit记录,每条记录包括
- 新增哪些sstable文件
- 日志文件编号
- 删除哪些sstable文件
- 当前compaction的下标
- 操作seqNumber等信息
通过这些信息,leveldb变可以启动时创建一个空的version,不断apply这些记录。最终可以得到一个上次运行结束时的版本信息。
主要是记录当前manifest 的文件名。为什么需要这个?因为leveldb每次启动时,都会创建一个新的manifest文件,因此会出现很多个manifest文件。current则用来指出那个才是我们需要关心的文件。
leveldb写操作不是直接写入磁盘,而是先写入内存。加入写入到内存的数据还未来得及持久化,leveldb发生异常或者服务器宕机等会造成写入的数据丢失。因此,leveldb在写入内存之前会首先将所有的写操作写入日志文件中。每次写操作都是一次顺序写入,这样写效率高,整体写入性能好。此外,leveldb写操作的原子性也可以通过log来实现。
异常情况主要有以下几种:
1.写log完成,写内存未完成
2.写log期间进程异常
3.write操作完成后(写日志、写内存都完成)异常
4.immutable memtable持久化过程异常
5.其它压缩异常
第2种情况发生,数据库重启读取log时,发现异常日志数据则丢弃该条日志数据,即视作这次用户写入失败
第1、3、4情况发生时,都可以通过读取redo日志文件中记录的写入操作来完成数据库的恢复。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/15924.html