目录
- 列值过滤器
- SingleColumnValueFilter 单列值过滤器
- SingleColumnValueExcludeFilter 单列值排除过滤器
- rowkey过滤器
- rowkey前缀过滤器:PrefixFilter
- 列簇过滤器
- 列过滤器
- PageFilter 分页过滤器
- 分页过滤器 改进版
- 多过滤器综合使用
之前Hbase查询表中的数据都是通过 get 和 scan ,但是get只能查询一行数据,scan虽然能查询范围内的数据,但是这个范围的划分仅仅是依靠行键的范围,不能做到像mysql或者hive那样针对列值进行筛选查询等操作。
为了让Hbase也可以有更加高级的查询,Hbase有了过滤器这一概念
过滤器可以根据列族、列、版本等更多的条件来对数据进行过滤,
基于 HBase 本身提供的三维有序(行键,列,版本有序),这些过滤器可以高效地完成查询过滤的任务,带有过滤器条件的 RPC 查询请求会把过滤器分发到各个 RegionServer(这是一个服务端过滤器),这样也可以降低网络传输的压力。
使用过滤器至少需要两类参数:
一类是抽象的比较运算符,另一类是比较器
常见的比较运算符有:
常见的比较器有:
过滤器的作用是在服务端判断数据是否满足条件,然后只将满足条件的数据返回给客户端
过滤器的类型很多,但是可以分为两大类:
比较过滤器:可应用于rowkey、列簇、列、列值过滤器
- 列值过滤器:ValueFilter
- 列过滤器:QualifierFilter
- 列簇过滤器:FamilyFilter
- rowKey过滤器:RowFilter
专用过滤器:只能适用于特定的过滤器
- 单列值过滤器:SingleColumnValueFilter
- 列值排除过滤器:SingleColumnValueExcludeFilter
- rowkey前缀过滤器:PrefixFilter
- 分页过滤器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();
}
}
列值过滤器仅仅针对单元格中的值进行过滤,满足 比较运算符加上比较器 构成的过滤条件,则留下,否则为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);
}
结果为
与列值过滤器进行比较,虽然都是针对列的值进行比较,但
- 列值过滤器是针对全部列的值,而单列值过滤器可以指定具体的列。
- 列值过滤器返回的是全部的行,而单列值过滤器返回的是满足过滤条件的行
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);
}
再把SingleColumnValueExcludeFilter 都换成 SingleColumnValueFilter
可以看到返回的行都是一样的,只是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);
}
针对 rowkey 的过滤器
rowkey前缀过滤器:PrefixFilter
@Test
/**
* 过滤出rowkey(id)以150010088开头的学生
*/
public void PrefixFilter() throws IOException{
PrefixFilter prefixFilter = new PrefixFilter("150010088".getBytes());
print(prefixFilter);
}
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);
}
}
}
列过滤器
这里是将列过滤器与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);
}
}
}
这里筛选返回了所有列名为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);
}
}
虽然是叫做分页过滤器,但其实依旧把之前的所有数据都遍历了,所以效率很低
分页过滤器 改进版
@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);
}
}
多过滤器综合使用
@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);
}