我们可以从两个不同的角度来看待LevleDb:静态角度和动态角度。
从静态角度,可以假想整个系统正在运行过程中(不断插入删除读取数据),此时我们给LevelDb照相,从照片可以看到之前系统的数据在内存和磁盘中是如何分布的,处于什么状态等;
从动态的角度,主要是了解系统是如何写入一条记录,读出一条记录,删除一条记录的,同时也包括除了这些接口操作外的内部操作比如compaction,系统运行时崩溃后如何恢复系统等等方面。
对于分析一个数据库,也基本遵循这样的思路
构成LevelDb静态结构的包括六个主要部分:内存中的MemTable和Immutable MemTable以及磁盘上的几种主要文件:Current文件,Manifest文件,log文件以及SSTable文件。
当应用写入一条Key:Value记录的时候,LevelDb会先往log文件里写入,成功后将记录插进Memtable中,这样基本就算完成了写入操作,因为一次写入操作只涉及一次磁盘顺序写和一次内存写入,所以这是为何说LevelDb写入速度极快的主要原因
log的作用主要是用于系统崩溃恢复
有序的key值,对于其它Level的SSTable文件来说,则不会出现同一层级内.sst文件的key重叠现象,就是说Level L中任意两个.sst文件,那么可以保证它们的key值是不会重叠的
由于log采用32K作为一个block部分,必然出现数据拆分的问题,这时候,数据分为几个类型:FULL/FIRST/MIDDLE/LAST
SSTable
可以将.sst文件划分为数据存储区和数据管理区,数据存储区存放实际的Key:Value数据,数据管理区则提供一些索引指针等管理数据,目的是更快速便捷的查找相应的记录。两个区域都是在上述的分块基础上的,就是说文件的前面若干块实际存储KV数据,后面数据管理区存储管理数据。管理数据又分为四种不同类型:紫色的Meta Block,红色的MetaBlock 索引和蓝色的数据索引块以及一个文件尾部块
Data Block 内的 KV 记录是按照 Key 由小到大排列的,数据索引区的每条记录是对某个 Data Block 建立的索引信息,每条索引信息包含三个内容,以图 所示的数据块 i 的索引 Index i 来说:红色部分的第一个字段记载大于等于数据块 i 中最大的 Key 值的那个 Key,第二个字段指出数据块 i 在.sst 文件中的起始位置,第三个字段指出 Data Block i 的大小(有时候是有数据压缩的)。后面两个字段好理解,是用于定位数据块在文件中的位置的
关于数据记录,还有重启点
“重启点” 的意思是:在这条记录开始,不再采取只记载不同的 Key 部分,而是重新记录所有的 Key 值,假设 Key i+1 是一个重启点,那么 Key 里面会完整存储 “the color”,而不是采用简略的 “olor” 方式。Block 尾部就是指出哪些记录是这些重启点的。
MemTable
核心的数据结构是一个skiplist
关于k8s上的database的争论部分?
log 文件内是 key 无序的,而 Memtable 中是 key 有序的。
查找记录
从 Memtable 到 Immutable Memtable,再从 Immutable Memtable 到文件,而文件是从低 level 到高 level 这么一个查询路径
level 0的文件查找,同其他level文件的查找不同(level 0存在不同文件重叠key的情况,需要按新鲜度进行排序)
Compaction
当memTable满的时候,发生minor compaction. minor compaction 实现起来也很简单,就是按照 immutable memtable 中记录由小到大遍历,并依次写入一个 level 0 的新建 SSTable 文件中,写完后建立文件的 index 数据,这样就完成了一次 minor compaction。