目录

  • 列值过滤器
  • SingleColumnValueFilter 单列值过滤器
  • SingleColumnValueExcludeFilter 单列值排除过滤器
  • rowkey过滤器
  • rowkey前缀过滤器:PrefixFilter
  • 列簇过滤器
  • 列过滤器
  • PageFilter 分页过滤器
  • 分页过滤器 改进版
  • 多过滤器综合使用


之前Hbase查询表中的数据都是通过 get 和 scan ,但是get只能查询一行数据,scan虽然能查询范围内的数据,但是这个范围的划分仅仅是依靠行键的范围,不能做到像mysql或者hive那样针对列值进行筛选查询等操作。

为了让Hbase也可以有更加高级的查询,Hbase有了过滤器这一概念

过滤器可以根据列族、列、版本等更多的条件来对数据进行过滤,
基于 HBase 本身提供的三维有序(行键,列,版本有序),这些过滤器可以高效地完成查询过滤的任务,带有过滤器条件的 RPC 查询请求会把过滤器分发到各个 RegionServer(这是一个服务端过滤器),这样也可以降低网络传输的压力。
使用过滤器至少需要两类参数:
一类是抽象的比较运算符,另一类是比较器

常见的比较运算符有:

hbase rowkey 内容过滤 hbase查询条件过滤_hbase rowkey 内容过滤

常见的比较器有:

hbase rowkey 内容过滤 hbase查询条件过滤_big data_02


过滤器的作用是在服务端判断数据是否满足条件,然后只将满足条件的数据返回给客户端

过滤器的类型很多,但是可以分为两大类:

比较过滤器:可应用于rowkey、列簇、列、列值过滤器

  1. 列值过滤器:ValueFilter
  2. 列过滤器:QualifierFilter
  3. 列簇过滤器:FamilyFilter
  4. rowKey过滤器:RowFilter

专用过滤器:只能适用于特定的过滤器

  1. 单列值过滤器:SingleColumnValueFilter
  2. 列值排除过滤器:SingleColumnValueExcludeFilter
  3. rowkey前缀过滤器:PrefixFilter
  4. 分页过滤器PageFilter

过滤器决定了针对哪里展开过滤,而比较运算符与比较器加在一起决定了过滤条件

下面举一些例子来说明

列值过滤器

过滤条件:找出age>23的学生
ValueFilter:作用在每一个cell上,符合要求的cell不会被过滤

package Demo.hbaseFilter;
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.ByteArrayOutputStream;
import java.io.IOException;
public class demo1 {
    Connection conn;
    Admin admin;
    TableName studentTN;
    Table student;

    public void print(Filter filter) throws IOException{
        //创建一个方法用来打印scan根据过滤器查询表后返回的结果
        Scan scan = new Scan();
        scan.setFilter(filter);
        ResultScanner scanner = student.getScanner(scan);
        for (Result result : scanner) {
            //scan扫描返回的时多行数据,遍历循环每一行的数据
            //利用getrow方法取出rowkey
            //利用getValue方法取出这一行的value值,根据列簇和列确定一个单元格的值
            String id = Bytes.toString(result.getRow());
            String name = Bytes.toString(result.getValue("info".getBytes(),"name".getBytes()));
            String age = Bytes.toString(result.getValue("info".getBytes(),"age".getBytes()));
            String gender = Bytes.toString(result.getValue("info".getBytes(),"gender".getBytes()));
            String clazz = Bytes.toString(result.getValue("info".getBytes(),"clazz".getBytes()));
            System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
        }
    }

    @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);
        // 创建Admin对象用来操作表结构
        admin = conn.getAdmin();
        //获取要操作的表的名字
        studentTN = TableName.valueOf("student");
        //根据表名创建Table对象用来操作表数据
        student = conn.getTable(studentTN);
    }

    @Test
    //列值过滤器
    public void ValueFilter() throws IOException{
        //创建二进制比较器
        BinaryComparator binaryComparator = new BinaryComparator("23".getBytes());
        //列值过滤器
        ValueFilter valueFilter = new ValueFilter(CompareFilter.CompareOp.GREATER, binaryComparator);
        //利用上面创建的print方法打印scan结果
        print(valueFilter);

    }

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

hbase rowkey 内容过滤 hbase查询条件过滤_hbase_03


列值过滤器仅仅针对单元格中的值进行过滤,满足 比较运算符加上比较器 构成的过滤条件,则留下,否则为null

虽然这里给出的过滤条件是 age > 23 ,但是发现在代码里面根本没有指定age这一列,因此实际上,列值过滤器是与所有列的所有单元格进行比较。如果满足条件则保留数据,如果不满足则过滤掉该数据,查询时不满足过滤条件的单元格都为null

这里的 id 和 name 等列的值也能显示出现,是因为这里的比较器是按照字节数组进行比较,id和name里面的值都满足过滤条件,所以没有 变成null

SingleColumnValueFilter 单列值过滤器
@Test
    /**
	 * 可以指定一个列进行过滤
     * 该过滤器会将符合过滤条件的列对应的cell所在的整行数据进行返回
     * 如果某条数据的列不符合条件,则会将整条数据进行过滤
     * 如果数据中不存在指定的列,则默认会直接返回,并且该列全为null
     *
     * 过滤条件:age > 23 的学生
     */
    public void SingleColumnValueFilter() throws IOException{
        //括号里面的参数从上到下依次是
        //列簇,列名,比较运算符,比较值或者比较器
        //这里直接传了一个值,没有用到比较器
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(
                "info".getBytes(),
                "age".getBytes(),
                CompareFilter.CompareOp.GREATER,
                "23".getBytes()
        );
        print(singleColumnValueFilter);
    }

结果为

hbase rowkey 内容过滤 hbase查询条件过滤_hbase rowkey 内容过滤_04

与列值过滤器进行比较,虽然都是针对列的值进行比较,但

  1. 列值过滤器是针对全部列的值,而单列值过滤器可以指定具体的列。
  2. 列值过滤器返回的是全部的行,而单列值过滤器返回的是满足过滤条件的行
SingleColumnValueExcludeFilter 单列值排除过滤器
@Test
    /**
     * 与SingleColumnValueFilter相反,会排除掉指定的列,其他的列全部返回
     *
     * 过滤条件:找到所有文科班的学生
     */
    public void SingleColumnValueExcludeFilter() throws IOException{
        //前缀比较器,用来与指定值的前缀进行匹配,匹配一致则正确
        BinaryPrefixComparator binaryPrefixComparator = new BinaryPrefixComparator("文科".getBytes());

        //这里指定的参数里面,比较运算符是等于,比较器是看指定值的前缀的是否为“文科”
        //找到所有clazz的前缀为“文科”的行,然后返回这些行
        SingleColumnValueExcludeFilter singleColumnValueExcludeFilter = new SingleColumnValueExcludeFilter(
                "info".getBytes(),
                "clazz".getBytes(),
                CompareFilter.CompareOp.EQUAL,
                binaryPrefixComparator
        );
        print(singleColumnValueExcludeFilter);
    }

hbase rowkey 内容过滤 hbase查询条件过滤_filter_05


再把SingleColumnValueExcludeFilter 都换成 SingleColumnValueFilter

hbase rowkey 内容过滤 hbase查询条件过滤_filter_06


可以看到返回的行都是一样的,只是SingleColumnValueExcludeFilter单列值排除过滤器,会把符合条件的列的值都去掉。而SingleColumnValueFilter单列值过滤器会找到所有符合条件的列,然后顺带着把符合条件的列的单元格所在行一起返回

rowkey过滤器
@Test
    /**
     * 过滤条件:过滤出rowkey(id)以150010088开头的学生
     */
    public void rowkeyFilter() throws IOException{
        BinaryPrefixComparator binaryPrefixComparator = new BinaryPrefixComparator("150010088".getBytes());
        RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL,binaryPrefixComparator);
        print(rowFilter);
    }

hbase rowkey 内容过滤 hbase查询条件过滤_hbase_07


针对 rowkey 的过滤器

rowkey前缀过滤器:PrefixFilter
@Test
    /**
     * 过滤出rowkey(id)以150010088开头的学生
     */
    public void PrefixFilter() throws IOException{
        PrefixFilter prefixFilter = new PrefixFilter("150010088".getBytes());
        print(prefixFilter);
    }

hbase rowkey 内容过滤 hbase查询条件过滤_hbase_08


rowkey过滤器加上前缀比较器后,与rowkey前缀过滤器的效果相同

列簇过滤器

先插入一个新的列簇

hbase(main):003:0> alter 'student',{NAME=>'cf'}
hbase(main):010:0> put 'student','001','cf:name','wang'
hbase(main):011:0> put 'student','001','cf:age','12'
hbase(main):012:0> put 'student','002','cf:name','zhang'
hbase(main):013:0> put 'student','002','cf:age','14'

这里是将列簇过滤器和正则表达式比较器相结合

@Test
    /**
     * 通过正则表达式: c[A-Za-z] 第一个字符为c,之后的字符为任意字符
     * 过滤出符合条件的列簇下的所有cell
     */
    public void RegexFamilyFilter() throws IOException {
        //创建正则表达式比较器
        RegexStringComparator regexStringComparator = new RegexStringComparator("c[A-Za-z]");
        //创建过滤器
        FamilyFilter familyFilter = new FamilyFilter(CompareFilter.CompareOp.EQUAL, regexStringComparator);
        //打印结果
        Scan scan = new Scan();
        scan.setFilter(familyFilter);

        ResultScanner scanner = student.getScanner(scan);
        for (Result rs : scanner) {
            for (Cell cell : rs.listCells()) {
                String cf = Bytes.toString(CellUtil.cloneFamily(cell));
                String q = Bytes.toString(CellUtil.cloneQualifier(cell));
                String value = Bytes.toString(CellUtil.cloneValue(cell));
                System.out.println(cf + ":" + q + " " + value);
            }

        }

    }

hbase rowkey 内容过滤 hbase查询条件过滤_过滤器_09

列过滤器

这里是将列过滤器与SubstringComparator子串比较器相结合

@Test
    public void SubStringQualifierFilter() throws IOException {
        //创建一个子字符串比较器,用来判断指定值中是否包含某个子字符串
        SubstringComparator comparator = new SubstringComparator("name");
        //创建一个过滤器,作用是找到所有满足子字符串比较器判断结果的单元格cell
        QualifierFilter qualifierFilter = new QualifierFilter(CompareFilter.CompareOp.EQUAL, comparator);

        //进行scan扫描,并返回最终结果
        Scan scan = new Scan();
        scan.setFilter(qualifierFilter);

		//对结果集中的全部单元格cell进行打印输出
        ResultScanner scanner = student.getScanner(scan);
        for (Result rs : scanner) {
            for (Cell cell : rs.listCells()) {
                String cf = Bytes.toString(CellUtil.cloneFamily(cell));
                String name = Bytes.toString(CellUtil.cloneQualifier(cell));
                String value = Bytes.toString(CellUtil.cloneValue(cell));
                System.out.println(cf + ":" + name + " " + value);
            }
        }
    }

hbase rowkey 内容过滤 hbase查询条件过滤_hbase rowkey 内容过滤_10


这里筛选返回了所有列名为name的单元格,并且通过CellUtil获取单元格的列簇名,列名,列值,并打印输出

PageFilter 分页过滤器

过滤条件:获取第四页的数据,每页10条

@Test
    public void PageFilter1() throws IOException {
        int page = 4;
        int pageSize = 10;

        // 首先先获取第4页的第一条数据的rk,
        int page_first = (page - 1) * pageSize + 1;
        //这里传入的page_first的值为31,含义是pageSize,因此利用这个过滤器返回的结果会是前31行数据
        PageFilter pageFilter1 = new PageFilter(page_first);

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

        //根据pageFilter1扫描返回的结果集为第1到31行的数据
        //通过遍历结果集中的全部数据,并提取每一行数据的rowkey赋值给循环外面定义的rowkey
        //这样rowkey里面的值每次循环都刷新一次,最后剩下的就是第31行数据的rowkey
        String rowkey = null;
        ResultScanner scanner = student.getScanner(scan);
        for (Result rs : scanner) {
            rowkey = Bytes.toString(rs.getRow());
        }

        //前面获取了此页的第一行数据的rowkey,现在利用withStartRow从这一行开始
        //new PageFilter(pageSize),pageSize为10,因此从第31行开始数10行
        Scan scan1 = new Scan();
        scan1.withStartRow(rowkey.getBytes());
        PageFilter pageFilter2 = new PageFilter(pageSize);
        scan1.setFilter(pageFilter2);
        //上面给scan扫描的范围设置了两个条件
        //第一个条件是withStartRow,从第31行开始扫描
        //第二个条件是setFilter(pageFilter2)设置分页过滤器,只读取十行数据
        ResultScanner scanner2 = student.getScanner(scan1);
        //遍历scan扫描后返回的结果集
        for (Result rs : scanner2) {
            String id = Bytes.toString(rs.getRow());
            String name = Bytes.toString(rs.getValue("info".getBytes(), "name".getBytes()));
            String age = Bytes.toString(rs.getValue("info".getBytes(), "age".getBytes()));
            String gender = Bytes.toString(rs.getValue("info".getBytes(), "gender".getBytes()));
            String clazz = Bytes.toString(rs.getValue("info".getBytes(), "clazz".getBytes()));
            System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
        }

    }

hbase rowkey 内容过滤 hbase查询条件过滤_filter_11


虽然是叫做分页过滤器,但其实依旧把之前的所有数据都遍历了,所以效率很低

分页过滤器 改进版
@Test
    /**
     * 之前的分页过滤器,虽然输出结果看起来和分页一样,但实际上把分页前的数据都遍历了一遍
     * 因为在withStartRow之前,是遍历当前页之前所有行的rowkey才找到的当前页的第一行的rowkey
     * 因此做出改进,可以通过合理地设计Rowkey来实现分页的功能
     * 过滤条件:获取第五页的数据,每页10条
     */
    public void PageWithRowkey() throws IOException {
        int page = 5;
        int pageSize = 10;
        //设置第一条数据的rowkey值
        int baseId = 1500100000;
        //通过第一条数据的rowkey值加上41的方式,得出第41行数据的rowkey值
        int page_rk = baseId + (page - 1) * pageSize + 1;

        //给scan扫描范围设置两个条件,
        // 第一个条件从page_rk,也就是第41行开始扫描
        // 第二个条件setLimit设置只扫描pageSize值的行数,也就是10行
        Scan scan = new Scan();
        scan.withStartRow((page_rk + "").getBytes());
        scan.setLimit(pageSize);

        //循环遍历打印输出
        ResultScanner scanner = student.getScanner(scan);
        for (Result rs : scanner) {
            String id = Bytes.toString(rs.getRow());
            String name = Bytes.toString(rs.getValue("info".getBytes(), "name".getBytes()));
            String age = Bytes.toString(rs.getValue("info".getBytes(), "age".getBytes()));
            String gender = Bytes.toString(rs.getValue("info".getBytes(), "gender".getBytes()));
            String clazz = Bytes.toString(rs.getValue("info".getBytes(), "clazz".getBytes()));
            System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
        }


    }

hbase rowkey 内容过滤 hbase查询条件过滤_hbase_12

多过滤器综合使用
@Test
    /**
     * 多个过滤器综合使用
     * 过滤 gender为男,age>23,理科班的学生
     * 过条件过滤需要使用FilterList
     */
    public void MultipleFilter() throws IOException {
        //通过单列值过滤器,找出所有gender为男的行
        SingleColumnValueFilter filter1 = new SingleColumnValueFilter(
                "info".getBytes(),
                "gender".getBytes(),
                CompareFilter.CompareOp.EQUAL,
                "男".getBytes()
        );

        //通过单列值过滤器,找到所有age大于23的行
        SingleColumnValueFilter filter2 = new SingleColumnValueFilter(
                "info".getBytes(),
                "age".getBytes(),
                CompareFilter.CompareOp.GREATER,
                "23".getBytes()
        );

        //通过单列值过滤器,找出所有clazz前缀为理科的行
        SingleColumnValueFilter filter3 = new SingleColumnValueFilter(
                "info".getBytes(),
                "clazz".getBytes(),
                CompareFilter.CompareOp.EQUAL,
                new BinaryPrefixComparator("理科".getBytes())
        );

        //利用FilterList,将多个过滤器放在一起,一起过滤
        FilterList filterList = new FilterList();
        filterList.addFilter(filter1);
        filterList.addFilter(filter2);
        filterList.addFilter(filter3);

        print(filterList);
    }

hbase rowkey 内容过滤 hbase查询条件过滤_filter_13