Hbase-day03_hbase_过滤器
Hbase_过滤器
HBase 的基本 API,包括增、删、改、查等。
增、删都是相对简单的操作,与传统的 RDBMS 相比,这里的查询操作略显苍白,只能根据特性的行键进行查询(Get)或者根据行键的范围来查询(Scan)。
HBase 不仅提供了这些简单的查询,而且提供了更加高级的过滤器(Filter)来查询。过滤器可以根据列族、列、版本等更多的条件来对数据进行过滤,
基于 HBase 本身提供的三维有序(行键,列,版本有序),这些过滤器可以高效地完成查询过滤的任务,带有过滤器条件的 RPC 查询请求会把过滤器分发到各个 RegionServer(这是一个服务端过滤器),这样也可以降低网络传输的压力。
使用过滤器至少需要两类参数:
一类是抽象的操作符,另一类是比较器
作用
- 过滤器的作用是在服务端判断数据是否满足条件,然后只将满足条件的数据返回给客户端
- 过滤器的类型很多,但是可以分为三大类:
- 比较过滤器:可应用于rowkey、列簇、列、列值过滤器
- 专用过滤器:只能适用于特定的过滤器
- 包装过滤器:包装过滤器就是通过包装其他过滤器以实现某些拓展的功能。
比较过滤器
所有比较过滤器均继承自
CompareFilter
。创建一个比较过滤器需要两个参数,分别是比较运算符和比较器实例。
public CompareFilter(final CompareOp compareOp,final ByteArrayComparable comparator) {
this.compareOp = compareOp;
this.comparator = comparator;
}
比较运算符
-
LESS <
-
LESS_OR_EQUAL <=
-
EQUAL =
-
NOT_EQUAL <>
-
GREATER_OR_EQUAL >=
-
GREATER >
-
NO_OP 排除所有
常见的六大比较过滤器
BinaryComparator
按字节索引顺序比较指定字节数组,采用Bytes.compareTo(byte[])
BinaryPrefixComparator
通BinaryComparator,只是比较左端前缀的数据是否相同
NullComparator
判断给定的是否为空
BitComparator
按位比较
RegexStringComparator
提供一个正则的比较器,仅支持 EQUAL 和非EQUAL
SubstringComparator
判断提供的子串是否出现在中
代码演示
rowKey过滤器:RowFilter 行键过滤器
通过RowFilter与BinaryComparator过滤比rowKey 1500100010小的所有值出来
/**
* 行键过滤器
* 通过RowFilter与BinaryComparator过滤比rowKey 1500100010小的所有值出来
*/
@Test
public void RowFilter1(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
BinaryComparator binaryComparator = new BinaryComparator("1500100010".getBytes());
//创建一个行键过滤器的对象
RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.LESS, binaryComparator);
Scan scan = new Scan();
scan.setFilter(rowFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 专门用来打印数据的方法
*/
public void print(ResultScanner scanner) throws IOException {
Result rs = null;
while ((rs = scanner.next()) != null) {
String id = Bytes.toString(rs.getRow());
// List<Cell> cells = rs.listCells();
// System.out.print("id:" + id);
// System.out.print("\t");
// for (Cell cell : cells) {
//// String s = Bytes.toString(cell.getValue());
// String col = Bytes.toString(CellUtil.cloneQualifier(cell));
// String s = Bytes.toString(CellUtil.cloneValue(cell));
// System.out.print(col + ":" + s);
// System.out.print("\t");
// }
// System.out.println();
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);
}
}
列簇过滤器:FamilyFilter
通过FamilyFilter与SubstringComparator查询列簇名包含in的所有列簇下面的数据
/**
* 列簇过滤器案例1:通过FamilyFilter与SubstringComparator查询列簇名包含in的所有列簇下面的数据
*/
@Test
public void FamilyFilter1(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建一个比较器对象
//只要列簇名中包含了in,就把该列簇下的所有列查询出来
SubstringComparator substringComparator = new SubstringComparator("in");
//创建列簇过滤器
FamilyFilter familyFilter = new FamilyFilter(CompareFilter.CompareOp.EQUAL, substringComparator);
Scan scan = new Scan();
scan.setFilter(familyFilter);
//获取数据
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
通过FamilyFilter与 BinaryPrefixComparator 过滤出列簇以i开头的列簇下的所有数据
/**
* 列簇过滤器案例2:通过FamilyFilter与 BinaryPrefixComparator 过滤出列簇以i开头的列簇下的所有数据
*
*/
@Test
public void FamilyFilter2(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建前缀比较器
BinaryPrefixComparator binaryPrefixComparator = new BinaryPrefixComparator("i".getBytes());
//创建列簇过滤器
FamilyFilter familyFilter = new FamilyFilter(CompareFilter.CompareOp.EQUAL, binaryPrefixComparator);
Scan scan = new Scan();
scan.setFilter(familyFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
列过滤器:QualifierFilter
通过QualifierFilter与SubstringComparator查询列名包含ge的列的值
/**
* 列过滤器案例1:通过QualifierFilter与SubstringComparator查询列名包含ge的列的值
*
*/
@Test
public void QualifierFilter1(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建包含比较器
//age
//gender
SubstringComparator substringComparator = new SubstringComparator("ge");
//创建一个列过滤器
QualifierFilter qualifierFilter = new QualifierFilter(CompareFilter.CompareOp.EQUAL, substringComparator);
Scan scan = new Scan();
scan.setFilter(qualifierFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
过滤出列的名字中 包含 "am" 所有的列 及列的值
/**
*
* 列过滤器案例2:通过QualifierFilter与SubstringComparator查询列名包含ge的列的值
*/
@Test
public void QualifierFilter2(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
SubstringComparator substringComparator = new SubstringComparator("am");
//创建列过滤器
QualifierFilter qualifierFilter = new QualifierFilter(CompareFilter.CompareOp.EQUAL, substringComparator);
Scan scan = new Scan();
scan.setFilter(qualifierFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
列值过滤器:ValueFilter
通过ValueFilter与BinaryPrefixComparator过滤出所有的cell中值以 "张" 开头的学生
/**
* 列值过滤器案例1:通过ValueFilter与BinaryPrefixComparator过滤出所有的cell中值以 "张" 开头的学生
*/
@Test
public void ValueFilter1() {
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建前缀比较器
BinaryPrefixComparator binaryPrefixComparator = new BinaryPrefixComparator("张".getBytes());
//创建列值过滤器的对象
ValueFilter valueFilter = new ValueFilter(CompareFilter.CompareOp.EQUAL, binaryPrefixComparator);
Scan scan = new Scan();
scan.setFilter(valueFilter);
ResultScanner scanner = students.getScanner(scan);
//因为ResultScanner类继承了迭代器
//使用增强for循环遍历
// for (Result rs : scanner) {
// String id = Bytes.toString(rs.getRow());
// System.out.println("当前行的rowkey为:" + id);
// //继续增强for循环得到每一行中的每一个单元格(列)
// //获取一行中的所有单元格
// for (Cell cell : rs.listCells()) {
// //获取该单元格属于的列簇
// String family = Bytes.toString(CellUtil.cloneFamily(cell));
// //获取该单元格的列名
// String colName = Bytes.toString(CellUtil.cloneQualifier(cell));
// //获取该单元格的列值
// String value = Bytes.toString(CellUtil.cloneValue(cell));
// System.out.println(family + ":" + colName + "的值为:" + value);
// }
// }
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
过滤出文科的学生,只会返回以文科开头的数据列,其他列的数据不符合条件,不会返回
/**
* 列值过滤器案例2:> 过滤出文科的学生,只会返回以文科开头的数据列,其他列的数据不符合条件,不会返回
*/
@Test
public void ValueFilter12(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建正则比较器
RegexStringComparator regexStringComparator = new RegexStringComparator("^文科.*");
//创建列值过滤器
ValueFilter valueFilter = new ValueFilter(CompareFilter.CompareOp.EQUAL, regexStringComparator);
Scan scan = new Scan();
scan.setFilter(valueFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
专用过滤器
单列值过滤器:SingleColumnValueFilter
SingleColumnValueFilter会返回满足条件的cell所在行的所有cell的值(即会返回一行数据)
通过SingleColumnValueFilter与查询文科班所有学生信息
/**
* 单列值过滤器
* SingleColumnValueFilter会返回满足条件的cell所在行的所有cell的值(即会返回一行数据)
*
* 通过SingleColumnValueFilter与查询文科班所有学生信息
*/
@Test
public void SingleColumnValueFilter(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建一个正则比较器
RegexStringComparator regexStringComparator = new RegexStringComparator("^文科.*");
//创建单列值过滤器对象
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(
"info".getBytes(),
"clazz".getBytes(),
CompareFilter.CompareOp.EQUAL,
regexStringComparator
);
Scan scan = new Scan();
scan.setFilter(singleColumnValueFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 专门用来打印数据的方法
*/
public void print(ResultScanner scanner) throws IOException {
Result rs = null;
while ((rs = scanner.next()) != null) {
String id = Bytes.toString(rs.getRow());
List<Cell> cells = rs.listCells();
System.out.print("id:" + id);
System.out.print("\t");
for (Cell cell : cells) {
// String s = Bytes.toString(cell.getValue());
String col = Bytes.toString(CellUtil.cloneQualifier(cell));
String s = Bytes.toString(CellUtil.cloneValue(cell));
System.out.print(col + ":" + s);
System.out.print("\t");
}
System.out.println();
// 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);
}
}
列值排除过滤器:SingleColumnValueExcludeFilter
与SingleColumnValueFilter相反,会排除掉指定的列,其他的列全部返回
通过SingleColumnValueExcludeFilter与BinaryComparator查询文科一班所有学生信息,最终不返回clazz列
/**
* 列值排除过滤器
* 与SingleColumnValueFilter相反,会排除掉指定的列,其他的列全部返回
*
* 通过SingleColumnValueExcludeFilter与BinaryComparator查询文科一班所有学生信息,最终不返回clazz列
*/
@Test
public void SingleColumnValueExcludeFilter(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建一个二进制比较器
BinaryComparator binaryComparator = new BinaryComparator("文科一班".getBytes());
//创建一个列值排除过滤器
SingleColumnValueExcludeFilter singleColumnValueExcludeFilter = new SingleColumnValueExcludeFilter(
"info".getBytes(),
"clazz".getBytes(),
CompareFilter.CompareOp.EQUAL,
binaryComparator
);
Scan scan = new Scan();
scan.setFilter(singleColumnValueExcludeFilter);
ResultScanner scanner = students.getScanner(scan);
print(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
rowkey前缀过滤器:PrefixFilter
通过PrefixFilter查询以150010008开头的所有前缀的rowkey
/**
* rowkey前缀过滤器
*
* 通过PrefixFilter查询以150010008开头的所有前缀的rowkey
*/
@Test
public void PrefixFilter(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//创建rowkey前缀过滤器
PrefixFilter prefixFilter = new PrefixFilter("150010008".getBytes());
Scan scan = new Scan();
scan.setFilter(prefixFilter);
ResultScanner scanner = students.getScanner(scan);
print2(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
分页过滤器PageFilter
通过PageFilter查询三页的数据,每页10条
使用PageFilter分页效率比较低,每次都需要扫描前面的数据,直到扫描到所需要查的数据
可设计一个合理的rowkey来实现分页需求
# 注意事项:
客户端进行分页查询,需要传递 startRow(起始 RowKey),知道起始 startRow 后,就可以返回对应的 pageSize 行数据。这里唯一的问题就是,对于第一次查询,显然 startRow 就是表格的第一行数据,但是之后第二次、第三次查询我们并不知道 startRow,只能知道上一次查询的最后一条数据的 RowKey(简单称之为 lastRow)。
我们不能将 lastRow 作为新一次查询的 startRow 传入,因为 scan 的查询区间是[startRow,endRow) ,即前开后闭区间,这样 startRow 在新的查询也会被返回,这条数据就重复了。
同时在不使用第三方数据库存储 RowKey 的情况下,我们是无法通过知道 lastRow 的下一个 RowKey 的,因为 RowKey 的设计可能是连续的也有可能是不连续的。
由于 Hbase 的 RowKey 是按照字典序进行排序的。这种情况下,就可以在 lastRow 后面加上 0 ,作为 startRow 传入,因为按照字典序的规则,某个值加上 0 后的新值,在字典序上一定是这个值的下一个值,对于 HBase 来说下一个 RowKey 在字典序上一定也是等于或者大于这个新值的。
所以最后传入 lastRow+0,如果等于这个值的 RowKey 存在就从这个值开始 scan,否则从字典序的下一个 RowKey 开始 scan。
25 个字母以及数字字符,字典排序如下:
'0' < '1' < '2' < ... < '9' < 'a' < 'b' < ... < 'z'
需要注意的是在多台 Regin Services 上执行分页过滤的时候,由于并行执行的过滤器不能共享它们的状态和边界,所以有可能每个过滤器都会在完成扫描前获取了 PageCount 行的结果,这种情况下会返回比分页条数更多的数据,分页过滤器就有失效的可能。
/**
* 分页过滤器
* 通过PageFilter查询三页的数据,每页10条
*/
@Test
public void PageFilter() {
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
//定义要查询的页数
int pageNum = 3;
//定义每页的条数
int pageSize = 10;
Scan scan = new Scan();
//定义一开始的行
String current_page_start_row = "";
for (int i = 1; i <= pageNum; i++) {
System.out.println("====================当前是第" + i + "页===========================");
//创建一个分页过滤器
PageFilter pageFilter = new PageFilter(pageSize);
scan.setFilter(pageFilter);
ResultScanner scanner = students.getScanner(scan);
for (Result rs : scanner) {
current_page_start_row = Bytes.toString(rs.getRow());
//告诉扫描器是从哪一行开始获取数据
scan.withStartRow((current_page_start_row + 0).getBytes());
PageFilter pageFilter1 = new PageFilter(pageSize);
scan.setFilter(pageFilter1);
//获取id
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);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
包装过滤器
SkipFilter过滤器
SkipFilter包装一个过滤器,当被包装的过滤器遇到一个需要过滤的 KeyValue 实例时,则拓展过滤整行数据。下面是一个使用示例:
// 定义 ValueFilter 过滤器
Filter filter1 = new ValueFilter(CompareOperator.NOT_EQUAL,
new BinaryComparator(Bytes.toBytes("xxx")));
// 使用 SkipFilter 进行包装
Filter filter2 = new SkipFilter(filter1);
WhileMatchFilter过滤器
WhileMatchFilter 包装一个过滤器,当被包装的过滤器遇到一个需要过滤的 KeyValue 实例时,WhileMatchFilter 则结束本次扫描,返回已经扫描到的结果。下面是其使用示例:
@Test
public void baoZhuang1() throws IOException {
HTableInterface students = conn.getTable("students");
Filter filter1 = new RowFilter(CompareFilter.CompareOp.NOT_EQUAL,new BinaryComparator("1500100009".getBytes()));
//不做包装
Scan scan = new Scan();
scan.setFilter(filter1);
ResultScanner scanner1 = students.getScanner(scan);
Result rs = scanner1.next();
while (rs != null) {
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 + "\t" + name + "\t" + age + "\t" + gender + "\t" + clazz + "\t");
rs = scanner1.next();
}
System.out.println("--------------------");
// 使用 WhileMatchFilter 进行包装
Filter filter2 = new WhileMatchFilter(filter1);
scan.setFilter(filter2);
ResultScanner scanner = students.getScanner(scan);
Result rs2 = scanner.next();
while (rs2 != null) {
String id = Bytes.toString(rs2.getRow());
String name = Bytes.toString(rs2.getValue("info".getBytes(), "name".getBytes()));
String age = Bytes.toString(rs2.getValue("info".getBytes(), "age".getBytes()));
String gender = Bytes.toString(rs2.getValue("info".getBytes(), "gender".getBytes()));
String clazz = Bytes.toString(rs2.getValue("info".getBytes(), "clazz".getBytes()));
System.out.println(id + "\t" + name + "\t" + age + "\t" + gender + "\t" + clazz + "\t");
rs2 = scanner.next();
}
}
多过滤器综合查询
以上都是讲解单个过滤器的作用,当需要多个过滤器共同作用于一次查询的时候,就需要使用 FilterList
。FilterList
支持通过构造器或者 addFilter
方法传入多个过滤器。
通过运用4种比较器过滤出姓于,年纪大于23岁,性别为女,且是理科的学生。
/**
* 需求:1 通过运用4种比较器过滤出姓于,年纪大于23岁,性别为女,且是理科的学生。
*
* 正则比较器 RegexStringComparator
* 包含比较器 SubstringComparator
* 二进制前缀比较器 BinaryPrefixComparator
* 二进制比较器 BinaryComparator
*
*/
@Test
public void FilterData1(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
/**
* 第一个过滤器,过滤出是理科开头的班级
*/
RegexStringComparator regexStringComparator = new RegexStringComparator("^理科.*");
//单列值过滤器
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter("info".getBytes(), "clazz".getBytes(),
CompareFilter.CompareOp.EQUAL, regexStringComparator);
/**
* 第二个过滤器,过滤出性别是女生的
*/
SubstringComparator substringComparator = new SubstringComparator("女");
SingleColumnValueFilter singleColumnValueFilter1 = new SingleColumnValueFilter("info".getBytes(), "gender".getBytes(),
CompareFilter.CompareOp.EQUAL, substringComparator);
/**
* 第三个过滤器,过滤出年龄大于23岁的
*/
BinaryComparator binaryComparator = new BinaryComparator("20".getBytes());
SingleColumnValueFilter singleColumnValueFilter2 = new SingleColumnValueFilter("info".getBytes(), "age".getBytes(),
CompareFilter.CompareOp.GREATER, binaryComparator);
/**
* 第四个过滤器,过滤出姓于的学生
*/
BinaryPrefixComparator binaryPrefixComparator = new BinaryPrefixComparator("于".getBytes());
SingleColumnValueFilter singleColumnValueFilter3 = new SingleColumnValueFilter("info".getBytes(), "name".getBytes(),
CompareFilter.CompareOp.EQUAL, binaryPrefixComparator);
Scan scan = new Scan();
//要想实现多个需求同时过滤,就需要创建多个过滤器,添加到一个过滤器列表中
//然后将过滤器列表传给扫描器scan
FilterList filterList = new FilterList();
filterList.addFilter(singleColumnValueFilter);
filterList.addFilter(singleColumnValueFilter1);
filterList.addFilter(singleColumnValueFilter2);
filterList.addFilter(singleColumnValueFilter3);
scan.setFilter(filterList);
ResultScanner scanner = students.getScanner(scan);
print2(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
过滤出学号是以15001001开头的文科学生
/**
* 过滤出学号是以15001001开头的文科学生
*/
@Test
public void filterData2(){
try {
//获取表的实例
HTableInterface students = conn.getTable("students");
/**
* 创建第一个过滤器,过滤是以15001001开头的rowkey
*/
BinaryPrefixComparator binaryPrefixComparator = new BinaryPrefixComparator("15001001".getBytes());
//创建行键过滤器
RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, binaryPrefixComparator);
/**
* 创建第二个过滤器,过滤出文科的学生
*/
RegexStringComparator regexStringComparator = new RegexStringComparator("^文科.*");
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter("info".getBytes(), "clazz".getBytes(),
CompareFilter.CompareOp.EQUAL,
regexStringComparator);
FilterList filterList = new FilterList();
filterList.addFilter(rowFilter);
filterList.addFilter(singleColumnValueFilter);
Scan scan = new Scan();
scan.setFilter(filterList);
ResultScanner scanner = students.getScanner(scan);
print2(scanner);
} catch (IOException e) {
e.printStackTrace();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)