HBASE LSM树 以及针对读操作的优化方式(多路归并(compact),布隆过滤器)

        LSM存储引擎是在B+树的基础上衍生过来的,目的就是为了在读和写之间,提高写的性能。所以,LSM树的弊端也由此可见,对读并不是很友好,所以,针对LSM树,有后续compact,布隆过滤器,blockCache等优化方式。来弥补对读的查询。

        

        LSM树的索引一般由2部分构成,一部分是内存部分,一部分是磁盘部分。内存部分采用跳跃表来维护一个有序的KV集合,也就是memstore.随着内存不断数据写入,一旦内存占用超过一定的阈值,就把内存部分数据进行导出(这里的flush操作实则是通过两个跳跃表来完成的),形成一个有序的数据文件,存储在磁盘上,磁盘部分则是对应的hFile。

        keyValue存储格式

         LSM树中存储的是多个keyValue组成的集合。每一个KeyValue由一个字节数组来表示。如图

HBASE LSM树 以及针对读操作的优化方式(多路归并(compact),布隆过滤器)_第1张图片

            LSM索引结构

   HBASE LSM树 以及针对读操作的优化方式(多路归并(compact),布隆过滤器)_第2张图片

 

       在hbase实现中,memstore的数据达到某个级别的阈值之后,都会进行flush到disk中,形成一个file。(前提为了怕memstore内存数据丢失,会先将数据写入到所属regionServer的WAL日志中)这个file的存储也就是一个小的B+树,因为hbase一般是部署在hdfs上,hdfs不支持对文件的update操作,而且最终随着磁盘文件越来越多,对读的影响很大。所以内存flush到磁盘上的小树,定期也会合并成一个大树。来增强读操作的性能,整体上hbase就是用了lsm tree的思路。
         

        多路归并      
       

          为了优化读取操作的性能,hbase会进行两种类型的compact。

  1. 一种是major Compact。是将所有的HFile一次性多路归并成一个文件。这种方式的好处是,合并之后只有一个文件,这样读取的性能肯定是最高的。但它的问题是合并所有的文件可能需要很长的时间并消耗大量的IO贷款,所以major Compact不宜使用太频繁,适合周期性的跑。或者我们手动设置在闲时合并。
  2. 另一种是minor Compact.即选中少数几个Hfile,将他们多路归并成一个文件。这种方式的有点是可以进行局部的Compact,通过少量的IO减少文件个数,提高读取操作的性能。适合较高频率的跑。但它的缺点是只合并了局部的数据,对于那些全局删除操作,无法在合并过程中完全删除。

         多路归并原理

          比如现在我们有K个文件,其中第i个文件内存存储有 N 个正整数(这些整数在文件内按照从小到大的顺序排序)

         多路归并的算法原理就是对每个文件设计一个指针,取出K个指针中数值最小的一个(对应的K个文件),然后把最小的那个指针后移,接着继续找K个指针中数值最小的一个,继续后移指针...直到文件全部读完为止。如图

HBASE LSM树 以及针对读操作的优化方式(多路归并(compact),布隆过滤器)_第3张图片

     

  针对读取操作,还涉及到了布隆过滤器。

       布隆过滤器是由一个长度为N的01 数组组成的。首先将数组array每个元素初始设为0,对集合A中的每个元素w,做K次哈希,第i次哈希值对N取模得到一个index(i).即 index(i) = Hash_i(w) % N,将array数组中的array[index(i)] 置为1.最终变为一个这些元素为1的01数组。

       

    HBASE LSM树 以及针对读操作的优化方式(多路归并(compact),布隆过滤器)_第4张图片

 

       正是由于布隆过滤器只需占用极小的空间,便可给出 “可能存在” 和 “肯定不存在”的存在性判断。因此可以提前过滤掉很多不必要的数据块,从而节省了大量的磁盘IO。hbase的get操作就是通过运用低成本高效率的布隆过滤器来过滤大量无效数据块的,从而节省了大量磁盘IO。

       如果在表中设置了Bloomfilter,那么HBase会在生成StoreFile时包含一份bloomfilter结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。 

     布隆过滤器的3中类型:

  1. none :关闭布隆过滤器功能
  2. row    按照rowkey计算布隆过滤器的二进制串并存储。get查询时,必须带rowkey.
  3. rowcol   按照rowkey+family+qualifier这3个字段拼出byte[]来计算布隆过滤器值并存储。如果查询时,get可以指定到这3个字段,则肯定可以通过布隆过滤器提高性能。

    任何类型的get(基于rowkey或row+col)Bloom Filter的优化都能生效,关键是get的类型要匹配Bloom Filter的类型

    基于row的scan是没办法走Bloom Filter的。因为Bloom Filter是需要事先知道过滤项的。对于顺序scan是没有事先办法知道       rowkey的。而get是指明了rowkey所以可以用Bloom Filter,scan指明column同理。

   一般意义上的scan操作,是没法使用布隆过滤器提升性能的,因为布隆过滤器的key不确定。但是 row+col+qualify的scan可以借助布隆过滤器去掉不存在此qualify的storefile,也算是不错的优化了,而且指明qualify也能减少流量,因此scan尽量指明qualify

 

  关于BlockCache的内容较多,在后续文章补充。

 

你可能感兴趣的:(hbase)