这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。
LevelDB特点
LevelDB是一个键值对数据库,是一个持久化的有序的Map,它具有以下几个特点:
- key和value可以是任何的字节数组(在golang中可以存放byte[]类型)。
- 数据是根据key进行顺序存储的,而且可以自定义其排序顺序。
- 有三个基本操作是
put(key,value),get(key)和delete(key)。 - 一个原子的Batch可以做多个操作。
- 支持
Snapshot可以由用户建立一个快照。 - 支持
Snappy压缩数据。 - 同一时间只能由一个进程访问数据库,但是支持多线程访问。
Level原理
LevelDB按照存储来分,可以分为三个组件:
MemTable:
- MemTable是一个小的内存数据结构,相当于一个简单的Map,内部采用的跳表机制,达到了树的性能级别,不需要磁盘IO,所以读写的速度非常快,所以就达到了场景需求:高效的写性能。同时也需要对内存中的数据进行持久化。
WAL:
- WAL和Mysql中的WAL技术一样,将每次操作记录到日志中,在发生数据库宕机的时候进行恢复。因此在每次加入操作日志的时候,同样有三种选择(每次写入日志就进行sync;每次写入日志但不进行sync;写入日志后隔一秒进行sync)。特点:并不是随机写而是顺序写。
- WAL + MemTable的方式相当于实现了一个简单的Redis,但是由于大量日志的恢复时间太长,因此也要定期对数据库进行持久化。
SSTable:
- 将MemTable的镜像写入到磁盘有一个要求:需要具有快速地在磁盘上查询一个键的功能。LevelDB并没有使用B+树的思想即每次对磁盘中的镜像进行修改,而是每次产生一个新的MemTable镜像放入SSTable(Sorter String Table),每次将当前MemTable写入镜像并清空WAL(因为对应的日志已落盘)。
- 由于产生了多个小的SSTable文件,每次读取的时候加载到内存中进行读即可。
Compaction机制:
- 由于LevelDB的特点:每次都是追加而不是原地修改,所以会产生很多小的MemTable块镜像,因此每次都加载一个SSTable太慢,LevelDB采用了Compaction机制,将多个小的SSTable块归并起来,由原来的Levle 0变为Level 1(以此类推,每个Level大概是上一级的10倍),就是将原来的多个小块合并到一个大块中,这样就能保证逻辑上的顺序;但是同时又引入一个缺点,由于Level 1中的大小太大无法加到内存中,所以针对合并后的SSTable进行了分块,分成了小块的SSTable,这样即保证了逻辑的顺序性也保证了物理磁盘中的顺序性。
- 在进行查询的时候,LevelDB采用了minifest(一个树结构,存放在内存中)记录每个物理块的序号值,在Level 1以上的块中,首先通过二分计算出所要读取的数据在哪个小块中,然后加载对应小块到内存进行读取即可。