LevelDB学习之路--doc/impl.html

本文介绍了LevelDB的设计理念与核心实现机制,包括日志文件、排序表、压缩规则及垃圾回收等内容,着重解析了LevelDB如何通过多层次文件组织提高查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

doc里的文档就打算翻译到这,之后应该是针对源码的分析了。

doc/里还有一个benchmarks.html,主要是讲leveldb和SQLite3,Kyoto Cabinet's的性能对比,就不翻译了。

看了这个impl.html之后对于leveldb的实现还是完全没有概念,只是知道为何称为"level"DB,应该是采用了对数据划分为不同level这种策略,所以取名为LevelDB。

具体的每个部分理解都和模糊。一方面是因为翻译的时候作者的意思可能理解错了。

另外一方面是因为还没有看过实现的源码,没有结合源码空谈实现机制很虚。

所以在之后的源码分析中,对于翻译错误的部分我一定会及时修改,希望这样的反馈过程能够促进我的学习。


Files

leveldb的实现,在精神层面上是与Bigtable table的十分相似的。但是由于文件组织的不同,还是有些差异的。每个database都是通过目录下的一些文件来代表的。下面有很多不同类型的文件类型:


Log files
一个log文件(*.log)中存储的是一些最近的更新。每个更新都会被添加到当前的log文件文件中。当log文件达到了预先定义的一个大小(大约4MB),他会被转化为一个排好序的表,紧接着生成一个新的log文件用来存储之后的更新。

在内存中有一份对当前的log文件的拷贝,这个拷贝是以memtable的形式存储的。所以每次read操作都会先从这个内存的中拷贝中寻找,你通过read操作也可以读取最近的所有更新记录。


Sorted tables
一个sorted table(.sst)存储着根据key值排序后记录。每一个记录要么是一个key的value,要么是对这个key的删除标志(删除标志是用来覆盖旧的sorted table里存储的数据)。

这些sorted tables被划分为几个level。刚刚从log file生成的sorted table被分在一个特殊的level--young level(也可以称为level-0)。当level为young的文件达到一定数量(目前是4),就会把所有的young level的文件和那些与young level有重叠的key的level-1的文件重新整合在一起,产生一列新的level-1的文件。(一般每个level-1的文件大小为2MB)。

level为young的文件里可能包含了重复的key。但是level大于等于1的文件中的key肯定都是独一无二的。对于level为L的文件(L>=1),这些等级为L文件的总和一旦超过(10^L)MB,就进行下面的整合操作:


level为L的文件分别为L_I={L_I1,L_I2,L_I3,...L_In},evel为L+1的文件分别为L_II={L_II1,L_II2,...,L_IIn}。


举例:

L_II集合中,与L_I1有相同key的文件为{L_II1,L_II2,L_II4,L_II7}

L_I1+L_I2+...+L_In>(10^L)MB

就把 L_I1和L_II1,L_II2,L_II4,L_II7整合生成新的文件,新生成的文件划分到Level-(L+1)这个等级中。

这样的整合操作可以把young level中新更新的数据逐渐往下一个level中迁移(这样可以使查询的成本降低到最小)。

Manifest
一个Manifest文件中列出了以下内容:
1.组成每个level的sorted tables
2.对应的key的范围
3.其他重要的元数据
每当database重新打开,就会生成一个新的Manifest文件(每个Manifest类型把 的文件名中都会有一个数字)。Manifest文件的内容格式就像log文件的格式一样,每当服务状态发生改变(例如文件的添加或者删除)就会添加到Manifest的末尾。


Current
CURRENT是个简单的text文件,内容是最新的Manifest文件名。


Info logs
通知性质的消息会存储到LOG或者LOG.old文件中。


Others
还有其他作用复杂的文件,例如LOCK, *.dbtmp等。


Level 0
当写入log文件的数据量达到一个预定的值(默认的是1MB):
生成一个新的memtable和log文件,将之后的更新直接写入到这里。
在后台:
把先前在memtable中的内容写入到一个sstable中。
丢弃memtable.
删除旧的log文件和旧的memtable文件。
把新的sstable文件划分为level-0等级。


Compactions
当level-L的文件总和超过预定的大小后,后台线程就会对数据进行整合。从L等级的文件中挑选一个,把它和L+1等级中所有有重叠的文件整合在一起。对于Level-0的文件,整合规则比较特殊:把level-0中所有有重叠的文件都挑出来,然后和level-1中有重叠的文件一起整合。


在上面的整合操作中,我么挑选level-L的文件,生成level-L+1的文件。有以下规则:
当输入的level-L文件足够生成2MB的level-L+1文件时我们才执行上面的操作。
当新的level-L+1文件中,已经存在能够和10个level-L+2的文件重叠的时候,我们会对level-L+1进行整合。
第二条规则是为了使得之后对level-L+1的整合能够简单一些。
旧的文件被丢弃之后,新的文件被加入到服务状态。
对特定level文件之间的整合是通过key的空间来转换的。详细的解释,例如对于level-L文件的整合,我们记录下最后一次整合的key,在下一次整合level-L文件的时候我们挑选的文件的key是在上次记录的key之后的(如果不存在这样的文件,那就从第一个文件开始)。
整合的时候丢弃掉被重写的值,那些被标记为deletion的记录,如果在level更加高级的文件中并不存在的话,就可以直接删除了。


Timing
level-0的文件整合工作最坏情况下,我们将读取4个level-0的文件(每个1MB)和所有的level-1文件(一共10MB)。我们将会一共读取14MB的文件。
除了对levle-0以外,当我们挑选了一个level-L的2MB文件时,我们最坏情况下可能有12个level-L+1文件与这个文件重叠(其中10个是因为level-L+1的总规模是level-L的10倍,其他2个文件因为level-L的文件分布在level-L+1的时候可能不是对齐的,所以出现了两个边界的文件)。这样一来整合操作需要读写一共26MB的文件。现代的硬盘IO速率大致是100MB/s,对于最坏的整合情况用时大概是0.5s。

如果我们限制了后台对写入的速率,比如降低为原来的10%,那么我们的整合需要花费5s。如果真的限制了写入速率为10MB/s,我们可能需要建立更多的level-0文件(大约50个,一共50MB)。这样一来由于需要我们整合的文件数目上限提高了,我们的每次读取的时候费时将变多。


解决方法1:
当level-0文件的数量变多,我们可以提高log文件的转换门槛。但是随着门槛的提高,我们装载对应的memtable所需的内存就越大。
解决方法2:
当level-0文件的数量提高了,我们可以人为地减少写入的效率。
解决方法3:
我们可以致力于减少大文件整合的费时。或许很多level-0的文件都有对应的block来缓存那些未经过压缩的数据。这样我们仅需考虑O(n)复杂度的整合迭代。


Number of files
文件的大小随着level的提高而提高,不能一直维持在2MB,这样才能减少level较高的文件的总数,尽管有时突发性的整合操作是很消费资源。我们可以选择在多个文件目录中共享同一系列的文件。

一个装载着ext3的文件系统的设备,目录中装载的文件大小都是100k,打开目录的耗时统计如下:


Files in directoryMicroseconds to open a file
1000 9
10000 10

100000 16


所以,分片操作对于现代的文件系统可能不是必须的。


Recovery
.从CURRENT文件中读取出最后更新的MANIFEST文件名。
.读取MANIFEST文件的内容。
.清除过期的文件。
.这个时候我们本可以打开所有的sstable,但是最好不要这样做。
.把log记录转化为level-0的sstable
.开始恢复的数据写入一个新的log中。


Garbage collection of files
在每次文件整合或者数据恢复之后都调用DeleteObsoleteFiles()方法。这个方法将找出database中所有的文件。删除所有非不是最新的log文件。还删除所有不是level文件引用的或者不是文件整合需要使用的table。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值