day 51 hbase 过滤 器布隆过滤器


I know, i know
地球另一端有你陪我




一、过滤器

为了配合 hbase 查找数据时更加人性化,提供了过滤器进数据的过滤

普通的过滤器需要配合比较运算符和比较器进行使用

1、常见的比较运算符

  • LESS <

  • LESS_OR_EQUAL <=

  • EQUAL =

  • NOT_EQUAL <>

  • GREATER_OR_EQUAL >=

  • GREATER >

  • NO_OP 排除所有

2、常见的比较器

BinaryComparator

按字节索引顺序比较指定字节数组,采用Bytes.compareTo(byte[])

BinaryPrefixComparator

通BinaryComparator,只是比较左端前缀的数据是否相同

RegexStringComparator

提供一个正则的比较器,仅支持 EQUAL 和非EQUAL

SubstringComparator

判断提供的子串是否出现在中


3、常见过滤器


rowKey过滤器:RowFilter

列簇过滤器:FamilyFilter

列过滤器:QualifierFilter

列值过滤器:ValueFilter


4、专用过滤器

单列值过滤器:SingleColumnValueFilter

SingleColumnValueFilter会返回满足条件的cell所在行的所有cell的值(即会返回一行数据)

列值排除过滤器:SingleColumnValueExcludeFilter

与SingleColumnValueFilter类似,会排除掉指定的列,其他的列全部返回

rowkey前缀过滤器:PrefixFilter

比对前缀是否相同

分页过滤器PageFilter

近似 scan 中的 limit


5、多过滤器 FilterList


全在代码里

package day51;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

public class FilterTest {

    Connection conn;
    Admin admin;
    TableName studentTN;
    Table students;

    @Before
    public void createConn() throws IOException {
        // 1、创建一个配置文件
        Configuration conf = HBaseConfiguration.create();
        // 配置ZK的地址,通过ZK可以找到HBase
        conf.set("hbase.zookeeper.quorum", "master:2181,node1:2181,node2:2181");

        // 2、创建连接
        conn = ConnectionFactory.createConnection(conf);
        // 3、创建Admin对象
        admin = conn.getAdmin();
        studentTN = TableName.valueOf("students");
        students = conn.getTable(studentTN);
    }

    public void print(Filter filter) throws IOException {

        Scan scan = new Scan();
        scan.setFilter(filter);

        ResultScanner scanner = students.getScanner(scan);
        for (Result rs : scanner) {

            String id = Bytes.toString(rs.getRow());
            String name = Bytes.toString(rs.getValue("cf1".getBytes(), "name".getBytes()));
            String age = Bytes.toString(rs.getValue("cf1".getBytes(), "age".getBytes()));
            String gender = Bytes.toString(rs.getValue("cf1".getBytes(), "gender".getBytes()));
            String clazz = Bytes.toString(rs.getValue("cf1".getBytes(), "clazz".getBytes()));
            System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
        }
    }

    public void printCell(Filter filter) throws IOException{

        Scan scan = new Scan();
        scan.setFilter(filter);
        ResultScanner scanner = students.getScanner(scan);
        for (Result rs : scanner) {
            for (Cell cell : rs.listCells()) {
                String id = Bytes.toString(CellUtil.cloneRow(cell));
                String f = Bytes.toString(CellUtil.cloneFamily(cell));
                String q = Bytes.toString(CellUtil.cloneQualifier(cell));
                String v = Bytes.toString(CellUtil.cloneValue(cell));
                System.out.println(id + "," + f + ":" + q + " " + v);
            }
        }
    }


    @Test
    // BinaryComparator 二进制比较器,完全匹配

    // ValueFilter 列值过滤器,会过滤每一个列值,即cell
    // 因此只要一条数据有一个cell满足条件就会被留下,没啥大用
    // 此处尝试 age > 23
    public void BinaryComparatorTest() throws IOException {
        // 创建二进制比较器
        BinaryComparator bct 
        	= new BinaryComparator("23".getBytes());

        // 创建列值过滤器(比较运算符,比较器)
        ValueFilter vft 
        	= new ValueFilter(CompareFilter.CompareOp.GREATER, bct);

        print(vft);
    }


    @Test
    // SingleColumnValueFilter 单列值过滤器
    // 指定表,列簇:列比较其下的值
    // 一次比较一条,因此不会有上一个的问题
    // age > 23 的学生
    public void SingleColumnValueFilterTest() throws IOException {

        SingleColumnValueFilter scvft = new SingleColumnValueFilter(
                // 列簇,列名
                "cf1".getBytes(), "age".getBytes(),
                // 比较运算符
                CompareFilter.CompareOp.GREATER,
                // 比较值
                "23".getBytes());

        print(scvft);
    }


    @Test
    // 和单列值基本相同,只是会过滤掉被比较的列
    public void SingleColumnValueExcludeFilterTest() 
    throws IOException {

        SingleColumnValueExcludeFilter scveft 
        			= new SingleColumnValueExcludeFilter(
                // 列簇,列名
                "cf1".getBytes(), "age".getBytes(),
                // 比较运算符
                CompareFilter.CompareOp.GREATER,
                // 比较值
                "23".getBytes());

        print(scveft);
    }


    @Test
    // BinaryPrefixComparator 二进制前缀比较器
    // 前缀进行匹配

    // RowFilter rowkey过滤器
    // 查找学号 15001009 开头的学生
    public void BinaryPrefixComparator() throws IOException {
        // 二进制前缀比较器
        BinaryPrefixComparator bpct
                = new BinaryPrefixComparator("15001009".getBytes());

        // rowkey 过滤器
        RowFilter rft 
        		= new RowFilter(CompareFilter.CompareOp.EQUAL, bpct);

        print(rft);
    }


    @Test
    // PrefixFilter:rowkey 前缀过滤器
    // 锁定过滤 rowkey
    // 相当于BinaryPrefixComparator + RowFilter
    public void PrefixFilter() throws IOException {

        // 由于只能比较 rowkey 因此不需要其他参数
        PrefixFilter pfft = new PrefixFilter("15001009".getBytes());

        print(pfft);
    }


    @Test
    // RegexStringComparator 正则比较器
    // 例:[A-Za-z0-9]{1}f[0-9]+
    // 1个(A-Za-z0-9)的 + f + 1到N个(0-9)

    // FamilyFilter 列簇过滤器
    // 过滤出符合条件的列簇下的所有cell
    // 这里用这个[a-z]+[0-9]+
    public void RegexStringComparatorTest() throws IOException {

        // 正则过滤器(String)
        RegexStringComparator rsct 
        	= new RegexStringComparator("[a-z]+[0-9]+");

        FamilyFilter fft 
        = new FamilyFilter(CompareFilter.CompareOp.EQUAL, rsct);

        // 不同列簇下,每条数据的组成可能不同,通过 result 一个个 get
        // 往往会因为那一项为 null,造成空指针异常
        // 此时需要使用 listcell 对每一个cell进行提取
        printCell(fft);
    }


    @Test
    // SubstringComparator 子字符串比较器
    // 没啥好说的

    // QualifierFilter 列过滤器,比较列
    public void SubStringComparatorTest() throws IOException {

        SubstringComparator ssct 
        				= new SubstringComparator("na");

        QualifierFilter qfft 
        = new QualifierFilter(CompareFilter.CompareOp.EQUAL, ssct);

        printCell(qfft);
    }


    @Test
    // PageFilter 分页过滤器
    // 实际使用类似 scan 中的 limit,也没啥大用
    public void PageFilterTest() throws IOException {

        PageFilter pft = new PageFilter(20);

        print(pft);
    }


    @Test
    // 一页10行数据,试图获取第四页的数据
    // 即 31 - 40 行
    // 方法一 尝试用pagefilter硬搞
    public void pageTest1() throws IOException {


        int page = 4;
        int pagesize = 10;
        int page_first = (page - 1) * pagesize + 1;
        PageFilter pft = new PageFilter(page_first);

        Scan scan = new Scan();
        scan.setFilter(pft);

        String rowkey = null;
        ResultScanner scanner = students.getScanner(scan);

        // 取到第31条的rowkey
        for (Result rs : scanner) {
            rowkey = Bytes.toString(rs.getRow());
        }

        // 31条往下读一页就是第四页
        Scan scan1 = new Scan();
        scan1.withStartRow(rowkey.getBytes());
        PageFilter pageFilter2 = new PageFilter(pagesize);
        scan1.setFilter(pageFilter2);
        ResultScanner scanner2 = students.getScanner(scan1);
        for (Result rs : scanner2) {

            String id = Bytes.toString(rs.getRow());
            String name = Bytes.toString(rs.getValue("cf1".getBytes(), "name".getBytes()));
            String age = Bytes.toString(rs.getValue("cf1".getBytes(), "age".getBytes()));
            String gender = Bytes.toString(rs.getValue("cf1".getBytes(), "gender".getBytes()));
            String clazz = Bytes.toString(rs.getValue("cf1".getBytes(), "clazz".getBytes()));
            System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
        }

    }
    @Test
    // 方法二 scan limit搞
    public void pageTest2() throws IOException{

        int page = 4;
        int pagesize = 10;
        int baseId = 1500100000;
        int page_first = baseId + (page - 1) * pagesize + 1;


        Scan scan = new Scan();
        // 关于 int 转字符串快捷方法
        scan.withStartRow((page_first + "").getBytes());
        scan.setLimit(pagesize);

        ResultScanner scanner = students.getScanner(scan);
        for (Result rs : scanner) {

            String id = Bytes.toString(rs.getRow());
            String name = Bytes.toString(
            rs.getValue("cf1".getBytes(), "name".getBytes()));
            
            String age = Bytes.toString(
            rs.getValue("cf1".getBytes(), "age".getBytes()));
            
            String gender = Bytes.toString(
            rs.getValue("cf1".getBytes(), "gender".getBytes()));
            
            String clazz = Bytes.toString(
            rs.getValue("cf1".getBytes(), "clazz".getBytes()));
            
            System.out.println(
            id + "," + name + "," + age + "," + gender + "," + clazz);
        }
    }


    @Test
    // FilterList 多条件过滤
    public void FilterList() throws IOException {

        FilterList filterList = new FilterList();

        SingleColumnValueFilter filter1 
        				= new SingleColumnValueFilter(
                "cf1".getBytes(),
                "age".getBytes(),
                CompareFilter.CompareOp.EQUAL,
                "23".getBytes());

        SingleColumnValueFilter filter2 
        				= new SingleColumnValueFilter(
                "cf1".getBytes(),
                "clazz".getBytes(),
                CompareFilter.CompareOp.GREATER,
                new BinaryPrefixComparator("文科".getBytes()));

        SingleColumnValueFilter filter3 
        				= new SingleColumnValueFilter(
                "cf1".getBytes(),
                "gender".getBytes(),
                CompareFilter.CompareOp.EQUAL,
                "女".getBytes());

        filterList.addFilter(filter1);
        filterList.addFilter(filter2);
        filterList.addFilter(filter3);

        print(filterList);
    }


    @After
    public void close() throws IOException {
        admin.close();
        conn.close();
        students.close();
    }
}

关于布隆过滤器

Bloom Filter(布隆过滤器)是1970年由布隆提出,实际上是一个很长的二进制向量和一系列随机映射函数。

布隆过滤器可以用于检索一个元素是否在一个集合中,它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

在计算机科学中,我们常常会碰到时间换空间或者空间换时间的情况,即为了达到某一个方面的最优而牺牲另一个方面。Bloom Filter在时间空间这两个因素之外又引入了另一个因素:错误率。

在判断一个元素是否属于某个集合时,会有一定的错误率(宁杀错,不放过)
有可能把不属于这个集合的元素误认为属于这个集合(False Positive),
但不会把属于这个集合的元素误认为不属于这个集合(False Negative),
增加了错误率这个因素后,Bloom Filter 通过允许少量错误来节省大量的存储空间。

它的用法其实是很容易理解的,我们拿个HBase中应用的例子来说下,我们已经知道rowKey存放在HFile中,那么为了从一系列的HFile中查询某个rowkey,我们就可以通过 Bloom Filter 快速判断 rowkey 是否在这个HFile中,从而过滤掉大部分的HFile,减少需要扫描的Block

二进制向量使用数组来实现,初始状态下位数组每一位都为0
在这里插入图片描述
假如此时有一个集合S = {x1, x2, … xn},Bloom Filter使用k个独立的hash函数,分别将集合中的每一个元素映射到{1,…,m}的范围。对于任何一个元素,被映射到的数字作为对应的位数组的索引,该位会被置为1。比如元素x1被hash函数映射到数字8,那么位数组的第8位就会被置为1。

下图中集合S只有两个元素x和y,分别被3个hash函数进行映射,映射到的位置分别为(0,3,6)和(4,7,10),对应的位会被置为1
在这里插入图片描述
现在假如要判断另一个元素是否是在此集合中,只需要被这3个hash函数进行映射,查看对应的位置是否有0存在,如果有的话,表示此元素肯定不存在于这个集合,否则有可能存在。下图所示就表示z肯定不在集合{x,y}中
在这里插入图片描述

HFile 中和 Bloom Filter 相关的Block

Scanned Block Section(扫描HFile时被读取):
Bloom Block

Load-on-open-section(regionServer启动时加载到内存):
BloomFilter Meta Block、Bloom Index Block

Bloom Block:Bloom数据块,存储Bloom的位数组
Bloom Index Block:Bloom数据块的索引
BloomFilter Meta Block:从HFile角度看bloom数据块的一些元数据信息

HBase中每个HFile都有对应的位数组,KeyValue在写入HFile时会先经过几个hash函数的映射,映射后将对应的数组位改为1,get请求进来之后再进行hash映射,如果在对应数组位上存在0,说明该get请求查询的数据不在该HFile中。

HFile中的Bloom Block中存储的就是上面说得位数组,当HFile很大时,Data Block 就会很多,同时KeyValue也会很多,需要映射入位数组的rowKey也会很多,所以为了保证准确率,位数组就会相应越大,那Bloom Block也会越大,为了解决这个问题就出现了Bloom Index Block,一个HFile中有多个Bloom Block(位数组),根据rowKey拆分,一部分连续的Key使用一个位数组。这样查询rowKey就要先经过Bloom Index Block(在内存中)定位到Bloom Block,再把Bloom Block加载到内存,进行过滤。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值