Algorithms Behind Modern Storage Systems

最近看了关于存储系统背后算法的一篇文章,现简单翻译一下。原文链接

Algorithms Behind Modern Storage Systems

Different uses for read-optimized B-trees and write-optimized LSM-trees

Alex Petrov

现代存储系统背后的算法

——读优化B-树 与 写优化 LSM-树的不同使用

应用产生的数据量在越来越大,而伴随着数据量的增大,大规模存储也变得更富有挑战。每种数据库系统都有各自的取舍,而理解这些特点,对从众多选择中选出最佳的一个是很有帮助的。

每个应用在读写权重、一致性要求、延迟和存取模式等方面都是不同的。熟悉数据库和存储的内部体系架构,才能解释为什么系统按某种特定的方式运行,才能在问题出现时有针对的解决,才能按具体的工作量精细的调整数据库。

同时在各个方面优化一个系统是不可能做到的。在理想的世界里,存在能保证最佳读写性能,并且没有过量存储的数据结构,但现实中是不存在的。

本文近距离的观察了现代主流数据库中两种存储系统设计方法——读优化B-树和写优化LSM(Log-structured merge)-树,并描述他们的具体用例和取舍。

B-树

B-树是一种流行的有读优化索引的二叉树。它有各种变化类型并且被应用在许多数据库(包括MySQL InnoDB 和 Post供热SQL)和文件系统(HFS+,ext4 中的 HTrees)中。B-树中的B代表Bayer,原始数据结构的作者,或者代表Boeing,当时他在那里工作。

在一棵二叉树中,每一个节点都有两个子节点(一般称为左子节点和右子节点)。左右子树节点的key,分别比当前节点的key小和大。为了保持树的深度最小,二叉树必须保持平衡:当随机顺序的节点添加到树中,树一边的深度最终自然地比另一边大。

一种平衡二叉树的方法是使用旋转:重新排列节点,把较长的子树的父节点放在其子节点下,并把该子节点提高,放在其父节点的位置。图1展示了使用旋转平衡二叉树的一个例子。为了平衡该树,节点3作为中轴(树绕着它进行旋转),节点5,原来是根节点,同时是节点3的父节点,变为它的子节点。在旋转结束后,左子树的高度减1,同时右子树的高度加1。树的最大深度减小了。


二叉树在内存数据结构中最有用的。因为平衡()和小()在磁盘上效果不够好。B-树允许每个节点储存超过两个指针,并且通过匹配节点大小与页大小(例如:4KB)而对块设备也支持良好。当前的一些实现,甚至采用了跨越多页的大节点。

B-树有如下的性质:

  • 有序的。因此支持序列化扫描与简化查找。
  • 自平衡。在插入和删除时不需要对树进行平衡。
  • 对数查找时间。这使得B-树在数据库索引等对查找时间比较看重的场景下是一个好选择。
  • 可变的。

本文讨论B+树,B-树的一个现代变种,常用于数据库存储。B+树不同于原生B-树,(a)它有一层额外的叶节点,持有value值。(b)这些value值不能被保存在内部节点中。

B-树剖析

先查看一下B-树的构建基础,如图2所示。B-树有多种节点类型:根节点、内部节点和叶节点。根节点(顶部)没有父节点(不是任何节点的子节点)。内部节点(中部)既有父节点,又有子节点。它们连接起了根节点和叶节点。叶节点(底部)持有数据,并且没有子节点。图2描述了一个度数为4的B-树(四个指针,三个含有key的内部节点,与四个含有key/value对的叶节点)。


B-树有如下特点:

  • 度数——指向子节点的指针数(N)。根据指针数,根节点和内部节点持有 N-1 和 key 值。
  • 占用率——有多少指针指向节点已被持有的子节点。例如,如果树的度为N,节点目前持有 N/2 的指针,则占用度为50%.
  • 高度——B-树的层数,表明在查找时会经过多少指针。

树中的每个非叶节点,至多持有 N 个 key 值(索引项),把树分为点对点排列的 N+1 个子树。Ki 项的指针 i 指向一颗子树,其上的每个索引项均为Ki-1 <= Ksearch < Ki(K为keys的集合)。第一个和最后一个指针是特例,分别指向含有比最左子节点 K0小或等于 K0 的项的子树,或含有大于最右子节点 KN-1 的项的子树。叶节点也持有一个指针,指向同级的前一个和下一个节点,形成一个兄弟节点的双向链表。所有节点里的key都是有序的。

查找

在运行查找时,从根节点开始,顺着内部节点递归向下到叶子层。在每一层,查询空间都顺着子指针而缩减到子树的范围。图3展示了B-树中顺着两个 key 中间的指针,其中一个 Key 比查找项大或等于查找项,一个 Key 比查找项小,而进行一次从根到叶的查找过程。在运行查找时,定位查找到叶节点后即为完成。在范围扫描中,会遍历当前叶节点的 Keys 与 values,然后遍历兄弟叶节点,直到范围结束。


在复杂度方面,B-树保证查找的 log(n) 复杂度,因为在节点中查找 key 使用的是二分查找,如图4所示。二分查找用在字典中查找某个特定的字母就很容易解释。字典中的所有单词都是按字母顺序排列。首先从字典的正中间打开,如果查找的字母在字母顺序中“小于”当前打开的,就会继续在字典左半边中进行查找,想反,则会在字典右半边中查找。通过不断二分并选择其中一半,来持续减少剩余的页数范围,并最终找到想要的字母。每一步均二分查找空间,使得查找时间在对数范围内。B-树的查找复杂度为对数级,是因为在节点层 keys 是有序的,并且运用二分法来查找。这也是保证高占用率并使树统一化如此重要的原因。


插入、更新与删除

在运行插入时,首先要找到目标叶节点。上面提到的查找算法就被使用到了。在找到目标叶节点后,key 和 value 追加在其后。如果叶节点没有足够的空余空间,那么就发生了溢出。叶节点必须被切分为两个节点。这是通过分配一个新的叶节点,移动一半元素到其上,并给父节点追加到新分配叶节点的指针等操作来实现的。如果父节点也没有足够的空余空间,则父节点也会进行切分。切分操作会一直延续到根节点。如果根节点也发生溢出,则其上的内容会切分到新分配的节点,而根节点自身会进行重写来避免迁移。这也表明树(与其高度)总是通过切分根节点来进行增长。

LSM-树

日志结构化合并树(Log-Structured Merge-Tree)是一种不可变的、常驻磁盘的写优化数据结构。它在写入比查找和检索记录更频繁的系统中非常有用。LSM-树因为剔除随机插入、更新与删除而获得许多关注。

LSM-树剖析

为支持顺序写,LSM-树批量写入和更新一个常驻内存的表(通常通过允许对数级查找的数据结构,如二叉搜索树、跳跃表来实现),直到其容量达到临界值,然后写入磁盘(该操作称为冲出)。检索数据需要查询树的所有常驻磁盘部分,检查内存中的表,并在返回结果前进行合并。图5展示了LSM-树的结构:常驻内存的表用来写入。当内存中的表足够大,其有序的内容会写入磁盘。读取时,会对磁盘和常驻内存的表进行操作,并需要合并操作来对数据进行调和。


排序字符串表

许多现代的LSM-树实现(例如 RocksDB、Apache Cassandra)中通过SSTable(Sorted String Tables)来实现常驻内存的表。这是因为其简单性(易写、易查找,易读)与合并属性(在合并中,SSTable 顺序扫描并合并写出)。

SSTable 是一种常驻磁盘的、有序的不可变数据结构。在结构上,SSTable 划分为两部分:数据块与索引块,如图6所示。数据块由顺序写入的独特 key/value 对组成,按 key 排序。索引块包含映射到数据块指针的 keys,这些指针指向真实记录的位置。索引经常由为快速查找而格式优化的 B-树来实现,或为了定点查询而使用哈希表来实现。SSTable 中的每个 value 项,都有一个关联的时间戳,指定了插入或更新操作(通常不能进行区分)的写入时间或删除操作的移除时间。


SSTable 有许多很好的特性:

  • 定点查询(例如通过key来查找对应的value)可以通过查找主索引来快速实现



原子性与持久性


总结


存储系统评估


参考文献

1. Comer, D. 1979. The ubiquitous B-tree. Computing Surveys 11(2); 121-137;


2. Data Systems Laboratory at Harvard. The RUM Conjecture;


3. Graefe, G. 2011. Modern B-tree techniques. Foundations and Trends in Databases 3(4): 203-402;



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值