• HBase之八--(3):Hbase 布隆过滤器BloomFilter介绍


    布隆过滤器( Bloom filters)

    数据块索引提供了一个有效的方法,在访问一个特定的行时用来查找应该读取的HFile的数据块。但是它的效用是有限的。HFile数据块的默认大小是64KB,这个大小不能调整太多。

    如果你要查找一个短行,只在整个数据块的起始行键上建立索引无法给你细粒度的索引信息。例如,如果你的行占用100字节存储空间,一个64KB的数据块包含(64 * 1024)/100 = 655.53 = ~700行,而你只能把起始行放在索引位上。你要查找的行可能落在特定数据块上的行区间里,但也不是肯定存放在那个数据块上。这有多种情况的可能,或者该行在表里不存在,或者存放在另一个HFile里,甚至在MemStore里。这些情况下,从硬盘读取数据块会带来IO开销,也会滥用数据块缓存。这会影响性能,尤其是当你面对一个巨大的数据集并且有很多并发读用户时。

    布隆过滤器允许你对存储在每个数据块的数据做一个反向测试。当某行被请求时,先检查布隆过滤器看看该行是否不在这个数据块。布隆过滤器要么确定回答该行不在,要么回答它不知道。这就是为什么我们称它是反向测试。布隆过滤器也可以应用到行里的单元上。当访问某列标识符时先使用同样的反向测试。

    布隆过滤器也不是没有代价。存储这个额外的索引层次占用额外的空间。布隆过滤器随着它们的索引对象数据增长而增长,所以行级布隆过滤器比列标识符级布隆过滤器占用空间要少。当空间不是问题时,它们可以帮助你榨干系统的性能潜力。

    你可以在列族上打开布隆过滤器,如下所示:

    hbase(main)> create 'mytable',{NAME=>'colfam1',BLOOMFILTER=>'ROWCOL'}

    BLOOMFILTER参数的默认值是NONE。一个行级布隆过滤器用ROW打开,列标识符级布隆过滤器用ROWCOL打开。行级布隆过滤器在数据块里检查特定行键是否不存在,列标识符级布隆过滤器检查行和列标识符联合体是否不存在。ROWCOL布隆过滤器的开销高于ROW布隆过滤器。

    1、主要功能
    提高随机读的性能
     
    2、存储开销
    bloom filter的数据存在StoreFile的meta中,一旦写入无法更新,因为StoreFile是不可变的。Bloomfilter是一个列族(cf)级别的配置属性,如果你在表中设置了Bloomfilter,那么HBase会在生成StoreFile时包含一份bloomfilter结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。 
     
    3、控制粒度
    a)ROW
    根据KeyValue中的row来过滤storefile 
    举例:假设有2个storefile文件sf1和sf2, 
    sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) 
    sf2包含kv3(r3 cf:q1 v)、kv4(r4 cf:q1 v) 
    如果设置了CF属性中的bloomfilter为ROW,那么get(r1)时就会过滤sf2,get(r3)就会过滤sf1 
    b)ROWCOL
    根据KeyValue中的row+qualifier来过滤storefile
    举例:假设有2个storefile文件sf1和sf2, 
    sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) 
    sf2包含kv3(r1 cf:q2 v)、kv4(r2 cf:q2 v) 
    如果设置了CF属性中的bloomfilter为ROW,无论get(r1,q1)还是get(r1,q2),都会读取sf1+sf2;而如果设置了CF属性中的bloomfilter为ROWCOL,那么get(r1,q1)就会过滤sf2,get(r1,q2)就会过滤sf1
     
     
    4、常用场景
    1、根据key随机读时,在StoreFile级别进行过滤
    2、读数据时,会查询到大量不存在的key,也可用于高效判断key是否存在
     
     
     
    5、举例说明
    假设x、y、z三个key存在于table中,W不存在
    使用Bloom Filter可以帮助我们减少为了判断key是否存在而去做Scan操作的次数
    step1)分别对x、y、z运算hash函数取得bit mask,写到Bloom Filter结构中
    step2)对W运算hash函数,从Bloom Filter查找bit mask
       如果不存在:三个Bit位至少有一个为0,W肯定不存在该(Bloom Filter不会漏判)
       如果存在   :三个Bit位全部全部等于1,路由到负责W的Region执行scan,确认是否真的存在(Bloom Filter有极小的概率误判)
     
     
     
    6、源码解析
    1.get操作会enable bloomfilter帮助剔除掉不会用到的Storefile
    在scan初始化时(get会包装为scan)对于每个storefile会做shouldSeek的检查,如果返回false,则表明该storefile里没有要找的内容,直接跳过
    [java] view plain copy
     
    1. if (memOnly == false    
    2.             && ((StoreFileScanner) kvs).shouldSeek(scan, columns)) {    
    3.           scanners.add(kvs);    
    4. }  
    shouldSeek方法:如果是scan直接返回true表明不能跳过,然后根据bloomfilter类型检查。
    [java] view plain copy
     
    1. if (!scan.isGetScan()) {    
    2.         return true;    
    3. }    
    4. byte[] row = scan.getStartRow();    
    5. switch (this.bloomFilterType) {    
    6.   case ROW:    
    7.     return passesBloomFilter(row, 0, row.length, null, 0, 0);    
    8.    
    9.   case ROWCOL:    
    10.     if (columns != null && columns.size() == 1) {    
    11.       byte[] column = columns.first();    
    12.       return passesBloomFilter(row, 0, row.length, column, 0, column.length);    
    13.     }    
    14.     // For multi-column queries the Bloom filter is checked from the    
    15.     // seekExact operation.    
    16.     return true;    
    17.    
    18.   default:    
    19.     return true;  
    20. }  


    2.指明qualified的scan在配了rowcol的情况下会剔除不会用掉的StoreFile。
    对指明了qualify的scan或者get进行检查:seekExactly
    [java] view plain copy
     
    1. // Seek all scanners to the start of the Row (or if the exact matching row    
    2. // key does not exist, then to the start of the next matching Row).    
    3. if (matcher.isExactColumnQuery()) {    
    4.   for (KeyValueScanner scanner : scanners)    
    5.   scanner.seekExactly(matcher.getStartKey(), false);    
    6. else {    
    7.   for (KeyValueScanner scanner : scanners)    
    8.   scanner.seek(matcher.getStartKey());    
    9. }  
    如果bloomfilter没命中,则创建一个很大的假的keyvalue,表明该storefile不需要实际的scan
    [java] view plain copy
     
    1. public boolean seekExactly(KeyValue kv, boolean forward)    
    2.       throws IOException {    
    3.     if (reader.getBloomFilterType() != StoreFile.BloomType.ROWCOL ||    
    4.         kv.getRowLength() == 0 || kv.getQualifierLength() == 0) {    
    5.       return forward ? reseek(kv) : seek(kv);    
    6.     }    
    7.     
    8.     boolean isInBloom = reader.passesBloomFilter(kv.getBuffer(),    
    9.         kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(),    
    10.         kv.getQualifierOffset(), kv.getQualifierLength());    
    11.     if (isInBloom) {    
    12.       // This row/column might be in this store file. Do a normal seek.    
    13.       return forward ? reseek(kv) : seek(kv);    
    14.     }    
    15.     
    16.     // Create a fake key/value, so that this scanner only bubbles up to the top    
    17.     // of the KeyValueHeap in StoreScanner after we scanned this row/column in    
    18.     // all other store files. The query matcher will then just skip this fake    
    19.     // key/value and the store scanner will progress to the next column.    
    20.     cur = kv.createLastOnRowCol();    
    21.     return true;    
    22. }  


    这边为什么是rowcol才能剔除storefile纳,很简单,scan是一个范围,如果是row的bloomfilter不命中只能说明该rowkey不在此storefile中,但next rowkey可能在。而rowcol的bloomfilter就不一样了,如果rowcol的bloomfilter没有命中表明该qualifiy不在这个storefile中,因此这次scan就不需要scan此storefile了!
     
     
     
    7、总结
    1.任何类型的get(基于rowkey或row+col)Bloom Filter的优化都能生效,关键是get的类型要匹配Bloom Filter的类型
     
    2.基于row的scan是没办法走Bloom Filter的。因为Bloom Filter是需要事先知道过滤项的。对于顺序scan是没有事先办法知道rowkey的。而get是指明了rowkey所以可以用Bloom Filter,scan指明column同理。
     
    3.row+col+qualify的scan可以去掉不存在此qualify的storefile,也算是不错的优化了,而且指明qualify也能减少流量,因此scan尽量指明qualify。
  • 相关阅读:
    java实训
    二维数组转置
    java第四次上机
    建立一个窗体
    java第三次上机
    数据结构晚自习
    Java程序设计第一次作业
    JAVA程序设计的第一次作业
    java中窗体的转化
    数据结构串的使用
  • 原文地址:https://www.cnblogs.com/duanxz/p/3141609.html
Copyright © 2020-2023  润新知