HBase为什么使用LSM树

    一般的关系型数据库使用的都是B+树,而《HBase权威指南》中说到HBase使用的LSM树,所以本文就是想来了解一下使用LSM树的好处是啥。

 

先来回顾下B+树:

为什么不用二叉树、红黑树?

    因为二叉树结构中,每个节点至多会有两个子节点,当树的高度很高时,相应的磁盘访问次数就要增加,因为访问磁盘的速度是很慢的,从而导致查询效率低下。所以很自然的想法就是把树的高度降一降,所以B+树就出现了(平衡多路查找树,B+树是一种很适合外存的数据结构)。

磁盘读取时的结构:

磁盘由多个盘片组成,固定在一个主轴上,每个面上都有一个磁头(读写头)用于读取盘片上的数据。盘片以每分钟数千转的速度转动,这样就实现了盘片上的任意读写。

    每个盘片又被划分成数百到上千个同心圆,称为磁道。但是这些同心圆不是连续,而是被画成一段段的圆弧,每一段称为扇区,每个扇区的数据硬件读写的最小单位(对于软件(逻辑层面)来说,磁盘块时读写的最小单位,一般是扇区的数倍)。

   读写的时候是这么操作的,所有盘上同一个磁道构成一个柱面,同一个柱面所有磁道全部写完之后,才会转移到下一个柱面,所以数据的读写是按柱面进行的,而不是按盘面进行。一个磁道写满数据后,就在同一柱面的下一个盘面来写,一个柱面写满后,才移到下一个扇区开始写数据,读数据也按照这种方式进行,这样就提高了硬盘的读 / 写效率。越在外面的磁盘读取越快,因为磁头速度快,而且磁道长。

    为什么读写是按柱面进行的?是因为选取磁头只需要通过电子切换即可,而选取柱面则必须机械切换,电子切换相当快,比在机械上的磁头向邻近磁道移动快得多。所以只有在同一柱面所有的磁头全部读 / 写完毕后磁头才转移到下一柱面(同心圆再往里的柱面)。

硬盘结构图                      盘面结构图

    写的时候文件会进行多次修改,每次写的时候只能指到新的没写过的地方,所以文件是破碎的,更加增加了磁头的读取时间。

    内存和磁盘的随机访问速度,内存是磁盘的100万倍,大概100ns,磁盘是10ms(1ns=10^-9s)。

 

B+树在磁盘中是怎么存的:

B+树是B树的一种变体,一颗M=4阶B树结构如下:

HBase为什么使用LSM树_第1张图片

它满足如下条件:

1.根节点至少有两个子节点(M>=2)

2.每个节点有M-1个key,并且以升序排列

3.位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间

4.其它节点至少有M/2个子节点

 

B+树做了些改变:

非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中;

树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录;

HBase为什么使用LSM树_第2张图片

    因为非叶节点不存放数据,所以在内存页中能够存放更多的key!

    并且叶子节点都是相连的,遍历整棵树可以一次性遍历所有叶子节点(B树只能进行中序遍历)!且数据顺序排列且相连,便于区间查找(数据库使用B+树的主要原因,B树不支持)和搜索。

    一般文件系统或者数据库系统设计时,会使用磁盘预读写原理,将一个节点的大小设置等于一页,一次I/O就可以完全载入。

既然B树数适合磁盘存储,HBase为什么又使用LSM树呢?

B+树虽然适合在磁盘中存储,并且从原理上来看它的速度应该会很快,但是它并非是顺序读写磁盘,例如它的节点进行分裂操作时在内存中会拆成两个新的页表,存储到磁盘上很可能就是不连续的;或者其他更新插入删除等操作,需要循环利用磁盘快,也会造成不连续问题。这也是HBase不使用B+树的根本原因,不进行优化的话随机I/O太多,范围查询和大量随机写时尤其明显

 

LSM树(Log-Structured Merge Tree):

    LSM树本质是在读写之间取得平衡,它首先在内存中构建一颗有序的小树,随着小树的逐渐增大,达到一定阈值时会flush到磁盘上。所以LSM树不像B+树一样是一棵完整的大树,一棵LSM树就是一个个B+树合起来

    多次flush之后会形成多个数据存储文件,后台线程会按照配置自动将多个文件合并成一个,此时多颗小树就会被合并成一棵大树。

https://img-blog.csdn.net/20130508094524405

    但是读取时,由于不知道数据在哪棵小树上,因此必须遍历所有小树(所以才说LSM牺牲了部分读的性能),每棵小树内部数据是有序的。查询是先查内存中的部分,再去查磁盘上的部分。

    个人理解LSM树的内容就是HFile中的索引部分,详情可以看之前写的分析HFile的那篇文章,这个就不再进行细说了。

 

这也就解释了为什么要有WAL了:

    因为数据是先存在内存中的,不会立即写到磁盘上,断点数据就丢失了,所以为了保护内存中的数据需要写在磁盘上记录LogFile。当内存中的数据flush到磁盘上时,就可以删除掉相应的LogFile了。

 

这也就解释了为什么需要compact了:

    因为上面说了,读的时候是要遍历所有小树的,小树越多,读的性能就会越来越差,所以需要进行merge,将多颗小树变成一颗大树。

 

总结:

    所以个人觉得LSM树和B+树相比,LSM树放弃了部分读性能,使用顺序写来大幅提高写性能,适合写多读少以及大规模数据读取;而B+树更加适合读多写少的场景。

    HBase本身设计(应当说是底层Hadoop的设计)就是更加偏向于写多读少的场景,所以使用LSM更加合适。

 

ps:使用SSD的话就没有寻道问题了,对于B+树来说是一个很大的性能提升,对于LSM树来说相对B+树会小一点。

 

 

 

 

 

参考:

https://blog.csdn.net/hguisu/article/details/7408047(磁盘读写原理)

http://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html(B树与B+树)

https://blog.csdn.net/u010853261/article/details/78217823(LSM的优劣性)

 

你可能感兴趣的:(HBase使用)