LSM 算法

2018-10-11  本文已影响356人  jiangmo

原文:
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.44.2782&rep=rep1&type=pdf

Google的BigTable架构在分布式结构化存储方面大名鼎鼎,其中的MergeDump模型在读写之间找到了一个较好的平衡点,很好的解决了web scale数据的读写问题。

MergeDump的理论基础是LSM-Tree (Log-Structured Merge-Tree)

思想

LSM的思想,在于对数据的修改增量保持在内存中,达到指定的限制后将这些修改操作批量写入到磁盘中,相比较于写入操作的高性能,读取需要合并内存中最近修改的操作和磁盘中历史的数据,即需要先看是否在内存中,若没有命中,还要访问磁盘文件。

原理:把一颗大树拆分成N棵小树,数据先写入内存中,随着小树越来越大,内存的小树会flush到磁盘中。磁盘中的树定期做合并操作,合并成一棵大树,以优化读性能。

对应于使用LSM的Leveldb来说,对于一个写操作,先写入到memtable中,当memtable达到一定的限制后,这部分转成immutable memtable(不可写),当immutable memtable达到一定限制,将flush到磁盘中,即sstable.,sstable再进行compaction操作。

LSM思想非常朴素,就是将对数据的更改hold在内存中,达到指定的threadhold后将该批更改批量写入到磁盘,在批量写入的过程中跟已经存在的数据做rolling merge。

举个例子

拿update举个例子:
比如有1000万行数据,现在希望update table.a set addr='new addr' where pk = '833',如果使用B-Tree类似的结构操作,就需要:

  1. 找到该条记录所在的page
  2. load page到内存(如果恰好该page已经在内存中,则省略该步)
  3. 如果该page之前被修改过,则先flush page to disk
  4. 修改数据

上面的动作平均来说有两次disk I/O,如果采用LSM-Tree类似结构,则:

  1. 将需要修改的数据直接写入内存

可见这里是没有disk I/O的。但是,这样的话读的时候就费劲了,需要merge disk上的数据和memory中的修改数据,这显然降低了读的性能。

确实如此,所以作者其中有个假设,就是写入远大于读取的时候,LSM是个很好的选择。我觉得更准确的描述应该是”优化了写,没有显著降低读“,因为大部分时候我们都是要求读最新的数据,而最新的数据很可能还在内存里面,即使不在内存里面,只要不是那些更新特别频繁的数据,其I/O次数也是有限的。

所以LSM-Tree比较适合的应用场景是:insert数据量大,读数据量和update数据量不高且读一般针对最新数据。

浅析

内存的高效性是lsm的结构基础,我们在访问数据的时候速度肯定是内存大于磁盘的,这样的话为什么不全用内存呢?原因大家自己考虑下就行了,所以权衡下来还是需要用硬盘的,那么为了实现数据的快速插入和查询,存储应该怎么设计呢?
我们知道,要使一个表对查询的响应比较快,那么最主要的手段就是索引,但是索引多了就会影响数据插入的速度,这也是一种平衡,下面我们将分析lsm,看看它是设计了个完美的解决方案吗?

lsm tree解决了什么问题? 它解决了随机读写创景下,减少数据频繁的插入、修改、删除操作所需要的磁盘I/O次数

学过数据结构的同学都知道Btree树是很多索引的优先选择结构,b tree树访问的时间复杂度接近Logm(N/2),可以计算下,在成百上千的索引节点下,即使索引十几亿的数据,那么树的深度也不会很深的,应该是10以内吧,再加上对于lru算法的支持,可以很明显的减少io,那为什么hbase不用这个结构呢,答案就是本文开头的几句话。

因为hbase中数据插入是比较随机的或者说是无序的,在查询数据的时候回到索引上,也就是对于某个叶子节点的访问是很随机的,这个场景很重要,那么我们根据这个具体场景分析一下b+树,因为查询是随机的,那么也就是说我们上次调入内存的数据可能很久以后都不会被访问,所以lru算法失去了它的价值,主要的系统开销变成了访问B+树的io了,内存的命中率很低,对于插入数据来说道理是一样的。

再看看lsm tree是怎么做的:

lsm构造许多小的结构,每个结构在内存里排序一下构成内部有序,查询的时候对每个小结构就可以采用二分法高效的查找定位,我们都知道有序的东西查找起来速度肯定比无序的快,如果只是这么设计肯定不能达到快速插入和查询的目的,lsm还引入了Bloom filter和小树到大树的排序

Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,通过多hash函数判断一个元素是否属于这个集合。 Bloom Filter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。

什么是lsm tree:
LSM-Tree通过使用一个基于内存的组件C0和一至多个基于磁盘的(C1, C2, …, CK)组件算法,对索引变更进行延迟及批量处理,并通过归并的方式高效的将更新迁移到磁盘。(有点耐个)

原理

Write & Read

Basic Compaction

为了保持LSM的读操作相对较快,维护并减少sstable文件的个数是很重要的,所以让我们更深入的看一下合并操作。这个过程有一点儿像一般垃圾回收算法。

Levelled Compaction


更新的实现,像 LevelDB 和 Cassandra解决这个问题的方法是:

按层合并的策略相对于上述的按文件大小合并的策略有二个关键的不同:

小结

Ref:
https://www.zhihu.com/question/19887265
http://f.dataguru.cn/thread-24939-1-1.html

上一篇 下一篇

猜你喜欢

热点阅读