Hbase过滤器
Hbase 提供种类丰富的过滤器(filter)来提高数据处理的效率,用户通过内置或自定义的过滤器来对数据进行过滤,所有过滤器都在服务端生效,即谓词下推(predicate push down)。保证过滤掉的数据不会被传送到客户端,从而减轻网络传输和客户端处理的压力。

Filter接口和FilterBase抽象类
Filter 接口定义过滤器基本方法,FilterBase 抽象类实现 Filter 接口。所有内置的过滤器则直接或者间接继承自 FilterBase 抽象类。用户只需要将定义好的过滤器通过 setFilter 方法传递给 Scan 或 put 的实例即可。
setFilter(Filter filter)
// Scan 中定义的 setFilter @Override public Scan setFilter(Filter filter) { super.setFilter(filter); return this; }
// Get 中定义的 setFilter @Override public Get setFilter(Filter filter) { super.setFilter(filter); return this; }
HBase 内置过滤器可以分为三类:分别是比较过滤器,专用过滤器和包装过滤器
比较过滤器
比较过滤器均继承自 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 (排除所有符合条件的值)
比较运算符均定义在枚举类 CompareOperator 中
@InterfaceAudience.Public public enum CompareOperator { LESS, LESS_OR_EQUAL, EQUAL, NOT_EQUAL, GREATER_OR_EQUAL, GREATER, NO_OP, }
比较器
比较器均继承自 ByteArrayComparable 抽象类
- BinaryComparator : 使用
Bytes.compareTo(byte [],byte [])按字典序比较指定的字节数组。 - BinaryPrefixComparator : 按字典序与指定的字节数组进行比较,但只比较到这个字节数组的长度。
- RegexStringComparator : 使用给定的正则表达式与指定的字节数组进行比较。仅支持
EQUAL和NOT_EQUAL操作。 - SubStringComparator : 测试给定的子字符串是否出现在指定的字节数组中,比较不区分大小写。仅支持
EQUAL和NOT_EQUAL操作。 - NullComparator :判断给定的值是否为空。
- BitComparator :按位进行比较
在进行 EQUAL 的比较时,如果比较器传入的是 abcd 的字节数组,但是待比较数据是 abcdefgh:
- 如果使用的是
BinaryPrefixComparator比较器,则比较以abcd字节数组的长度为准,即efgh不会参与比较,这时候认为abcd与abcdefgh是满足EQUAL条件的; - 如果使用的是
BinaryComparator比较器,则认为其是不相等的。
比较过滤器种类
- RowFilter :基于行键来过滤数据;
- FamilyFilterr :基于列族来过滤数据;
- QualifierFilterr :基于列限定符(列名)来过滤数据;
- ValueFilterr :基于单元格 (cell) 的值来过滤数据;
- DependentColumnFilter :指定一个参考列来过滤其他列的过滤器,过滤的原则是基于参考列的时间戳来进行筛选
只要传递比较运算符和运算器实例即可构建,然后通过 setFilter 方法传递给 scan:
Filter filter = new RowFilter(CompareOperator.LESS_OR_EQUAL,new BinaryComparator(Bytes.toBytes("xxx"))); scan.setFilter(filter);
DependentColumnFilter
把 DependentColumnFilter 理解为一个 valueFilter 和一个时间戳过滤器的组合。DependentColumnFilter 有三个带参构造器,选择一个参数最全的进行说明:
DependentColumnFilter(final byte [] family, final byte[] qualifier, final boolean dropDependentColumn, final CompareOperator op, final ByteArrayComparable valueComparator)
- family :列族
- qualifier :列限定符(列名)
- dropDependentColumn :决定参考列是否被包含在返回结果内,为 true 时表示参考列被返回,为 false 时表示被丢弃
- op :比较运算符
- valueComparator :比较器
DependentColumnFilter dependentColumnFilter = new DependentColumnFilter( Bytes.toBytes("student"), Bytes.toBytes("name"), false, CompareOperator.EQUAL, new BinaryPrefixComparator(Bytes.toBytes("xiaolan")));
-
首先会去查找
student:name中值以xiaolan开头的所有数据获得参考数据集,这一步等同于 valueFilter 过滤器; -
其次再用参考数据集中所有数据的时间戳去检索其他列,获得时间戳相同的其他列的数据作为
结果数据集,这一步等同于时间戳过滤器; -
最后如果
dropDependentColumn为 true,则返回参考数据集+结果数据集,若为 false,则抛弃参考数据集,只返回结果数据集
专用过滤器
专用过滤器通常直接继承自 FilterBase,适用于范围更小的筛选规则。
单列列值过滤器 (SingleColumnValueFilter)
基于某列(参考列)的值决定某行数据是否被过滤。其实例有以下方法:
- setFilterIfMissing(boolean filterIfMissing) :默认值为 false,即如果该行数据不包含参考列,其依然被包含在最后的结果中;设置为 true 时,则不包含;
- setLatestVersionOnly(boolean latestVersionOnly) :默认为 true,即只检索参考列的最新版本数据;设置为 false,则检索所有版本数据。
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter( "student".getBytes(), "name".getBytes(), CompareOperator.EQUAL, new SubstringComparator("xiaolan")); singleColumnValueFilter.setFilterIfMissing(true); scan.setFilter(singleColumnValueFilter);
行键前缀过滤器 (PrefixFilter)
基于 RowKey 值决定某行数据是否被过滤。
PrefixFilter prefixFilter = new PrefixFilter(Bytes.toBytes("xxx")); scan.setFilter(prefixFilter);
基于列限定符(列名)决定某行数据是否被过滤。
ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("xxx")); scan.setFilter(columnPrefixFilter);
使用这个过滤器实现对结果按行进行分页,创建 PageFilter 实例的时候需要传入每页的行数
public PageFilter(final long pageSize) { Preconditions.checkArgument(pageSize >= 0, "must be positive %s", pageSize); this.pageSize = pageSize; }
客户端进行分页查询,需要传递 startRow(起始 RowKey),知道起始 startRow 后,就可以返回对应的 pageSize 行数据。这里唯一的问题就是,对于第一次查询,显然 startRow 就是表格的第一行数据,但是之后第二次、第三次查询我们并不知道 startRow,只能知道上一次查询的最后一条数据的 RowKey
不能将 lastRow 作为新一次查询的 startRow 传入,因为 scan 的查询区间是[startRow,endRow) ,即前开后闭区间,这样 startRow 在新的查询也会被返回,这条数据就重复。
同时在不使用第三方数据库存储 RowKey 的情况下,我们是无法通过知道 lastRow 的下一个 RowKey 的,因为 RowKey 的设计可能是连续的也有可能是不连续的。
由于 Hbase 的 RowKey 是按照字典序进行排序的。这种情况下,就可以在 lastRow 后面加上 0 ,作为 startRow 传入,因为按照字典序的规则,某个值加上 0 后的新值,在字典序上一定是这个值的下一个值,对于 HBase 来说下一个 RowKey 在字典序上一定也是等于或者大于这个新值的。
byte[] POSTFIX = new byte[] { 0x00 }; Filter filter = new PageFilter(15); int totalRows = 0; byte[] lastRow = null; while (true) { Scan scan = new Scan(); scan.setFilter(filter); if (lastRow != null) { // 如果不是首行 则 lastRow + 0 byte[] startRow = Bytes.add(lastRow, POSTFIX); System.out.println("start row: " + Bytes.toStringBinary(startRow)); scan.withStartRow(startRow); } ResultScanner scanner = table.getScanner(scan); int localRows = 0; Result result; while ((result = scanner.next()) != null) { System.out.println(localRows++ + ": " + result); totalRows++; lastRow = result.getRow(); } scanner.close(); //最后一页,查询结束 if (localRows == 0) break; } System.out.println("total rows: " + totalRows);
时间戳过滤器 (TimestampsFilter)
List<Long> list = new ArrayList<>(); list.add(1554975573000L); TimestampsFilter timestampsFilter = new TimestampsFilter(list); scan.setFilter(timestampsFilter);
首次行键过滤器 (FirstKeyOnlyFilter)
FirstKeyOnlyFilter 只扫描每行的第一列,扫描完第一列后就结束对当前行的扫描,并跳转到下一行。相比于全表扫描,其性能更好,通常用于行数统计的场景,因为如果某一行存在,则行中必然至少有一列。
FirstKeyOnlyFilter firstKeyOnlyFilter = new FirstKeyOnlyFilter(); scan.set(firstKeyOnlyFilter);
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 则结束本次扫描,返回已经扫描到的结果。
Filter filter1 = new RowFilter(CompareOperator.NOT_EQUAL, new BinaryComparator(Bytes.toBytes("rowKey4"))); Scan scan = new Scan(); scan.setFilter(filter1); ResultScanner scanner1 = table.getScanner(scan); for (Result result : scanner1) { for (Cell cell : result.listCells()) { System.out.println(cell); } } scanner1.close(); System.out.println("--------------------"); // 使用 WhileMatchFilter 进行包装 Filter filter2 = new WhileMatchFilter(filter1); scan.setFilter(filter2); ResultScanner scanner2 = table.getScanner(scan); for (Result result : scanner1) { for (Cell cell : result.listCells()) { System.out.println(cell); } } scanner2.close();
当需要多个过滤器共同作用于一次查询的时候,就需要使用 FilterList。FilterList 支持通过构造器或者 addFilter 方法传入多个过滤器。
// 构造器传入 public FilterList(final Operator operator, final List<Filter> filters) public FilterList(final List<Filter> filters) public FilterList(final Filter... filters) // 方法传入 public void addFilter(List<Filter> filters) public void addFilter(Filter filter)
多个过滤器组合的结果由 operator 参数定义 ,其可选参数定义在 Operator 枚举类中。只有 MUST_PASS_ALL 和 MUST_PASS_ONE 两个可选的值:
- MUST_PASS_ALL :相当于 AND,必须所有的过滤器都通过才认为通过;
- MUST_PASS_ONE :相当于 OR,只有要一个过滤器通过则认为通过。
List<Filter> filters = new ArrayList<Filter>(); Filter filter1 = new RowFilter(CompareOperator.GREATER_OR_EQUAL, new BinaryComparator(Bytes.toBytes("XXX"))); filters.add(filter1); Filter filter2 = new RowFilter(CompareOperator.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes("YYY"))); filters.add(filter2); Filter filter3 = new QualifierFilter(CompareOperator.EQUAL, new RegexStringComparator("ZZZ")); filters.add(filter3); FilterList filterList = new FilterList(filters); Scan scan = new Scan(); scan.setFilter(filterList);
浙公网安备 33010602011771号