1. 程式人生 > >HBase - Filter - 過濾器的介紹以及使用

HBase - Filter - 過濾器的介紹以及使用

nbsp 附加 4.3 之一 ring 來看 字典 qualifier 使用情況

1 過濾器
HBase 的基本 API,包括增、刪、改、查等。
增、刪都是相對簡單的操作,與傳統的 RDBMS 相比,這裏的查詢操作略顯蒼白,只能根據特性的行鍵進行查詢(Get)或者根據行鍵的範圍來查詢(Scan)。
HBase 不僅提供了這些簡單的查詢,而且提供了更加高級的過濾器(Filter)來查詢。

1.1 過濾器的兩類參數
過濾器可以根據列族、列、版本等更多的條件來對數據進行過濾,基於 HBase 本身提供的三維有序(行鍵,列,版本有序),這些過濾器可以高效地完成查詢過濾的任務,帶有過濾器條件的 RPC 查詢請求會把過濾器分發到各個 RegionServer(這是一個服務端過濾器),這樣也可以降低網絡傳輸的壓力。
使用過濾器至少需要兩類參數:

1.1.1 一類是抽象的操作符
HBase 提供了枚舉類型的變量來表示這些抽象的操作符:
LESS
LESS_OR_EQUAL
EQUAL
NOT_EQUAL
GREATER_OR_EQUAL
GREATER
NO_OP

1.1.2 另一類是比較器
代表具體的邏輯,例如字節級的比較,字符串級的比較等。

1.2 比較器
比較器作為過濾器的核心組成之一,用於處理具體的比較邏輯,例如字節級的比較,字符串級的比較等。

1.2.1 RegexStringComparator
支持正則表達式的值比較

Scan scan = new Scan();
RegexStringComparator comp = new RegexStringComparator("you."); // 以 you 開頭的字符串
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.2.2 SubStringComparator
用於監測一個子串是否存在於值中,並且不區分大小寫。

Scan scan = new Scan();
SubstringComparator comp = new SubstringComparator("1129"); // 查找包含 1129 的字符串
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.2.3 BinaryPrefixComparator
前綴二進制比較器。與二進制比較器不同的是,只比較前綴是否相同。

Scan scan = new Scan();
BinaryPrefixComparator comp = new BinaryPrefixComparator(Bytes.toBytes("yting")); //
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.2.4 BinaryComparator
二進制比較器,用於按字典順序比較 Byte 數據值。
Scan scan = new Scan();
BinaryComparator comp = new BinaryComparator(Bytes.toBytes("xmei")); //
ValueFilter filter = new ValueFilter(CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.3 列值過濾器

1.3.1 SingleColumnValueFilter
SingleColumnValueFilter 用於測試值的情況(相等,不等,範圍 、、、)

下面一個檢測列族 family 下的列 qualifier 的列值和字符串 "my-value" 相等的部分示例代碼 :
Scan scan = new Scan();
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, Bytes.toBytes("my-value"));
scan.setFilter(filter);

1.3.2 SingleColumnValueExcludeFilter
跟 SingleColumnValueFilter 功能一樣,只是不查詢出該列的值。

下面的代碼就不會查詢出 family 列族下 qualifier 列的值(列都不會查出來)
Scan scan = new Scan();
SingleColumnValueExcludeFilter filter = new SingleColumnValueExcludeFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, Bytes.toBytes("my-value"));
scan.setFilter(filter);


1.4 鍵值元數據過濾器
HBase 采用 "鍵值對" 保存內部數據,鍵值元數據過濾器評估一行的 "鍵" 是否保存在(如 ColumnFamily:Column qualifiers)。

1.4.1 FamilyFilter
用於過濾列族(通常在 Scan 過程中通過設定某些列族來實現該功能,而不是直接使用該過濾器)。

Scan scan = new Scan();
FamilyFilter filter = new FamilyFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("my-family"))); // 列族為 my-family
scan.setFilter(filter);

1.4.2 QualifierFilter
用於列名(Qualifier)過濾。

Scan scan = new Scan();
QualifierFilter filter = new QualifierFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("my-column"))); // 列名為 my-column
scan.setFilter(filter);

1.4.3 ColumnPrefixFilter
用於列名(Qualifier)前綴過濾,即包含某個前綴的所有列名。

Scan scan = new Scan();
ColumnPrefixFilter filter = new ColumnPrefixFilter(Bytes.toBytes("my-prefix")); // 前綴為 my-prefix
scan.setFilter(filter);

1.4.4 MultipleColumnPrefixFilter
MultipleColumnPrefixFilter 與 ColumnPrefixFilter 的行為類似,但可以指定多個列名(Qualifier)前綴。

Scan scan = new Scan();
byte[][] prefixes = new byte[][]{Bytes.toBytes("my-prefix-1"), Bytes.toBytes("my-prefix-2")};
MultipleColumnPrefixFilter filter = new MultipleColumnPrefixFilter(prefixes); // 不解釋,你懂的 、、、
scan.setFilter(filter);

1..4.5 ColumnRangeFilter
該過濾器可以進行高效的列名內部掃描。(為何是高效呢???因為列名是已經按字典排序好的)HBase-0.9.2 版本引入該功能。

Scan scan = new Scan();
boolean minColumnInclusive = true;
boolean maxColumnInclusive = true;
ColumnRangeFilter filter = new ColumnRangeFilter(Bytes.toBytes("minColumn"), minColumnInclusive, Bytes.toBytes("maxColumn"), maxColumnInclusive);
scan.setFilter(filter);

1.6 DependentColumnFilter
該過濾器嘗試找到該列所在的每一行,並返回該行具有相同時間戳的全部鍵值對。

Scan scan = new Scan();
DependentColumnFilter filter = new DependentColumnFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"));
scan.setFilter(filter);

1.5 行鍵過濾器

1.5.1 RowFilter
行鍵過濾器,一般來講,執行 Scan 使用 startRow/stopRow 方式比較好,而 RowFilter 過濾器也可以完成對某一行的過濾。

Scan scan = new Scan();
RowFilter filter = new RowFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("my-row-1")));
scan.setFilter(filter);

1.5.2 RandomRowFilter
該過濾器是隨機選擇一行的過濾器。參數 chance 是一個浮點值,介於 0.1 和 1.0 之間。

Scan scan = new Scan();
float chance = 0.5f;
RandomRowFilter filter = new RandomRowFilter(chance); // change 在 0.1 ~ 1.0 之間的浮點值
scan.setFilter(filter);

1.6 功能過濾器

1.6.1 PageFilter
用於按行分頁。

long pageSize = 10;
int totalRowsCount = 0;
PageFilter filter = new PageFilter(pageSize);
byte[] lastRow = null;
while(true) {
Scan scan = new Scan();
scan.setFilter(filter);
if(lastRow != null) {
byte[] postfix = Bytes.toBytes("postfix");
byte[] startRow = Bytes.add(lastRow, postfix);
scan.setStartRow(startRow);
System.out.println("start row : " + Bytes.toString(startRow));
}

ResultScanner scanner = _hTable.getScanner(scan);
int localRowsCount = 0;
for(Result result : scanner) {
System.out.println(localRowsCount++ + " : " + result);
totalRowsCount++;
lastRow = result.getRow(); // ResultScanner 的結果集是排序好的,這樣就可以取到最後一個 row 了
}
scanner.close();

if(localRowsCount == 0) break;
}
System.out.println("total rows is : " + totalRowsCount);

1.6.2 FirstKeyOnlyFilter
該過濾器只查詢每個行鍵的第一個鍵值對,在統計計數的時候提高效率。(HBase-Coprocessor 做 RowCount 的時候可以提高效率)。
Scan scan = new Scan();
FirstKeyOnlyFilter filter = new FirstKeyOnlyFilter(); // 只查詢每個行鍵的第一個鍵值對
scan.setFilter(filter);

1.6.3 KeyOnlyFilter
Scan scan = new Scan();
KeyOnlyFilter filter = new KeyOnlyFilter(); // 只查詢每行鍵值對中有 "鍵" 元數據信息,不顯示值,可以提升掃描的效率
scan.setFilter(filter);

1.6.4 InclusiveStopFilter
常規的 Scan 包含 start-row 但不包含 stop-row,如果使用該過濾器便可以包含 stop-row。

Scan scan = new Scan();
InclusiveStopFilter filter = new InclusiveStopFilter(Bytes.toBytes("stopRowKey"));
scan.setFilter(filter);

1.6.5 ColumnPaginationFilter
按列分頁過濾器,針對列數量很多的情況使用。

Scan scan = new Scan();
int limit = 0;
int columnOffset = 0;
ColumnPaginationFilter filter = new ColumnPaginationFilter(limit, columnOffset);
scan.setFilter(filter);

2 自定義過濾器
做法 : 繼承 FilterBase,然後打成 jar 放到 $HBASE_HOEM/lib 目錄下去(註意:需要重啟 HBase 集群)

HBase為篩選數據提供了一組過濾器,通過這個過濾器可以在HBase中的數據的多個維度(行,列,數據版本)上進行對數據的篩選操作,也就是說過濾器最終能夠篩選的數據能夠細化到具體的一個存儲單元格上(由行鍵,列明,時間戳定位)。通常來說,通過行鍵,值來篩選數據的應用場景較多。


1. RowFilter:篩選出匹配的所有的行,對於這個過濾器的應用場景,是非常直觀的:使用BinaryComparator可以篩選出具有某個行鍵的行,或者通過改變比較運算符(下面的例子中是CompareFilter.CompareOp.EQUAL)來篩選出符合某一條件的多條數據,以下就是篩選出行鍵為row1的一行數據:

[java] view plain copy
Filter rf = new RowFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("row1"))); // OK 篩選出匹配的所有的行

2. PrefixFilter:篩選出具有特定前綴的行鍵的數據。這個過濾器所實現的功能其實也可以由RowFilter結合RegexComparator來實現,不過這裏提供了一種簡便的使用方法,以下過濾器就是篩選出行鍵以row為前綴的所有的行:
[java] view plain copy
Filter pf = new PrefixFilter(Bytes.toBytes("row")); // OK 篩選匹配行鍵的前綴成功的行

3. KeyOnlyFilter:這個過濾器唯一的功能就是只返回每行的行鍵,值全部為空,這對於只關註於行鍵的應用場景來說非常合適,這樣忽略掉其值就可以減少傳遞到客戶端的數據量,能起到一定的優化作用:
[java] view plain copy
Filter kof = new KeyOnlyFilter(); // OK 返回所有的行,但值全是空

4. RandomRowFilter:從名字上就可以看出其大概的用法,本過濾器的作用就是按照一定的幾率(<=0會過濾掉所有的行,>=1會包含所有的行)來返回隨機的結果集,對於同樣的數據集,多次使用同一個RandomRowFilter會返回不通的結果集,對於需要隨機抽取一部分數據的應用場景,可以使用此過濾器:
[java] view plain copy
Filter rrf = new RandomRowFilter((float) 0.8); // OK 隨機選出一部分的行

5. InclusiveStopFilter:掃描的時候,我們可以設置一個開始行鍵和一個終止行鍵,默認情況下,這個行鍵的返回是前閉後開區間,即包含起始行,但不包含終止行,如果我們想要同時包含起始行和終止行,那麽我們可以使用此過濾器:
[java] view plain copy
Filter isf = new InclusiveStopFilter(Bytes.toBytes("row1")); // OK 包含了掃描的上限在結果之內

6. FirstKeyOnlyFilter:如果你只想返回的結果集中只包含第一列的數據,那麽這個過濾器能夠滿足你的要求。它在找到每行的第一列之後會停止掃描,從而使掃描的性能也得到了一定的提升:
[java] view plain copy
Filter fkof = new FirstKeyOnlyFilter(); // OK 篩選出第一個每個第一個單元格

7. ColumnPrefixFilter:顧名思義,它是按照列名的前綴來篩選單元格的,如果我們想要對返回的列的前綴加以限制的話,可以使用這個過濾器:
[java] view plain copy
Filter cpf = new ColumnPrefixFilter(Bytes.toBytes("qual1")); // OK 篩選出前綴匹配的列

8. ValueFilter:按照具體的值來篩選單元格的過濾器,這會把一行中值不能滿足的單元格過濾掉,如下面的構造器,對於每一行的一個列,如果其對應的值不包含ROW2_QUAL1,那麽這個列就不會返回給客戶端:
[java] view plain copy
Filter vf = new ValueFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator("ROW2_QUAL1")); // OK 篩選某個(值的條件滿足的)特定的單元格

9. ColumnCountGetFilter:這個過濾器來返回每行最多返回多少列,並在遇到一行的列數超過我們所設置的限制值的時候,結束掃描操作:
[java] view plain copy
Filter ccf = new ColumnCountGetFilter(2); // OK 如果突然發現一行中的列數超過設定的最大值時,整個掃描操作會停止

10. SingleColumnValueFilter:用一列的值決定這一行的數據是否被過濾。在它的具體對象上,可以調用setFilterIfMissing(true)或者setFilterIfMissing(false),默認的值是false,其作用是,對於咱們要使用作為條件的列,如果這一列本身就不存在,那麽如果為true,這樣的行將會被過濾掉,如果為false,這樣的行會包含在結果集中。

[java] view plain copy
SingleColumnValueFilter scvf = new SingleColumnValueFilter(
Bytes.toBytes("colfam1"),
Bytes.toBytes("qual2"),
CompareFilter.CompareOp.NOT_EQUAL,
new SubstringComparator("BOGUS"));
scvf.setFilterIfMissing(false);
scvf.setLatestVersionOnly(true); // OK

11. SingleColumnValueExcludeFilter:這個與10種的過濾器唯一的區別就是,作為篩選條件的列的不會包含在返回的結果中。
12. SkipFilter:這是一種附加過濾器,其與ValueFilter結合使用,如果發現一行中的某一列不符合條件,那麽整行就會被過濾掉:

[java] view plain copy
Filter skf = new SkipFilter(vf); // OK 發現某一行中的一列需要過濾時,整個行就會被過濾掉

13. WhileMatchFilter:這個過濾器的應用場景也很簡單,如果你想要在遇到某種條件數據之前的數據時,就可以使用這個過濾器;當遇到不符合設定條件的數據的時候,整個掃描也就結束了:
[java] view plain copy
Filter wmf = new WhileMatchFilter(rf); // OK 類似於Python itertools中的takewhile

14. FilterList:用於綜合使用多個過濾器。其有兩種關系:FilterList.Operator.MUST_PASS_ONE和FilterList.Operator.MUST_PASS_ALL,默認的是FilterList.Operator.MUST_PASS_ALL,顧名思義,它們分別是AND和OR的關系,並且FilterList可以嵌套使用FilterList,使我們能夠表達更多的需求:

[java] view plain copy
List<Filter> filters = new ArrayList<Filter>();
filters.add(rf);
filters.add(vf);
FilterList fl = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); // OK 綜合使用多個過濾器, AND 和 OR 兩種關系

以上,是對於HBase內置的過濾器的部分總結,以下代碼是數據寫入代碼:

[java] view plain copy
package com.reyun.hbase;

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;

public class HBaseDataFeeding {
private final static byte[] ROW1 = Bytes.toBytes("row1");
private final static byte[] ROW2 = Bytes.toBytes("row2");
private final static byte[] COLFAM1 = Bytes.toBytes("colfam1");
private final static byte[] COLFAM2 = Bytes.toBytes("colfam2");
private final static byte[] QUAL1 = Bytes.toBytes("qual1");
private final static byte[] QUAL2 = Bytes.toBytes("qual2");


public static void main(String[] args) throws IOException {
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "testtable");
table.setAutoFlushTo(false);
Put put_row1 = new Put(ROW1);
put_row1.add(COLFAM1, QUAL1, Bytes.toBytes("ROW1_QUAL1_VAL"));
put_row1.add(COLFAM1, QUAL2, Bytes.toBytes("ROW1_QUAL2_VAL"));

Put put_row2 = new Put(ROW2);
put_row2.add(COLFAM1, QUAL1, Bytes.toBytes("ROW2_QUAL1_VAL"));
put_row2.add(COLFAM1, QUAL2, Bytes.toBytes("ROW2_QUAL2_VAL"));

try{
table.put(put_row1);
table.put(put_row2);
}finally{
table.close();
}
}

}

以下是過濾器測試代碼,可以通過修改代碼,更換過濾器來看到具體的效果:

[java] view plain copy
package com.reyun.hbase;

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

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.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
import org.apache.hadoop.hbase.filter.PageFilter;
import org.apache.hadoop.hbase.filter.PrefixFilter;
import org.apache.hadoop.hbase.filter.RandomRowFilter;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SkipFilter;
import org.apache.hadoop.hbase.filter.ValueFilter;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.filter.WhileMatchFilter;
import org.apache.hadoop.hbase.util.Bytes;

public class HBaseScannerTest {

public static void main(String[] args) throws IOException, IllegalAccessException {
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "testtable");
table.setAutoFlushTo(false);

Scan scan1 = new Scan();
SingleColumnValueFilter scvf = new SingleColumnValueFilter(
Bytes.toBytes("colfam1"),
Bytes.toBytes("qual2"),
CompareFilter.CompareOp.NOT_EQUAL,
new SubstringComparator("BOGUS"));
scvf.setFilterIfMissing(false);
scvf.setLatestVersionOnly(true); // OK
Filter ccf = new ColumnCountGetFilter(2); // OK 如果突然發現一行中的列數超過設定的最大值時,整個掃描操作會停止
Filter vf = new ValueFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator("ROW2_QUAL1")); // OK 篩選某個(值的條件滿足的)特定的單元格
Filter cpf = new ColumnPrefixFilter(Bytes.toBytes("qual2")); // OK 篩選出前綴匹配的列
Filter fkof = new FirstKeyOnlyFilter(); // OK 篩選出第一個每個第一個單元格
Filter isf = new InclusiveStopFilter(Bytes.toBytes("row1")); // OK 包含了掃描的上限在結果之內
Filter rrf = new RandomRowFilter((float) 0.8); // OK 隨機選出一部分的行
Filter kof = new KeyOnlyFilter(); // OK 返回所有的行,但值全是空
Filter pf = new PrefixFilter(Bytes.toBytes("row")); // OK 篩選匹配行鍵的前綴成功的行
Filter rf = new RowFilter(CompareFilter.CompareOp.NOT_EQUAL, new BinaryComparator(Bytes.toBytes("row1"))); // OK 篩選出匹配的所有的行
Filter wmf = new WhileMatchFilter(rf); // OK 類似於Python itertools中的takewhile
Filter skf = new SkipFilter(vf); // OK 發現某一行中的一列需要過濾時,整個行就會被過濾掉

List<Filter> filters = new ArrayList<Filter>();
filters.add(rf);
filters.add(vf);
FilterList fl = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); // OK 綜合使用多個過濾器, AND 和 OR 兩種關系

scan1.
setStartRow(Bytes.toBytes("row1")).
setStopRow(Bytes.toBytes("row3")).
setFilter(scvf);
ResultScanner scanner1 = table.getScanner(scan1);

for(Result res : scanner1){
for(Cell cell : res.rawCells()){
System.out.println("KV: " + cell + ", Value: " + Bytes.toString(CellUtil.cloneValue(cell)));
}
System.out.println("------------------------------------------------------------");
}

scanner1.close();
table.close();
}

}

引言 -- 參數基礎
有兩個參數類在各類Filter中經常出現,統一介紹下:
(1)比較運算符 CompareFilter.CompareOp
比較運算符用於定義比較關系,可以有以下幾類值供選擇:
EQUAL 相等
GREATER 大於
GREATER_OR_EQUAL 大於等於
LESS 小於
LESS_OR_EQUAL 小於等於
NOT_EQUAL 不等於

(2)比較器 ByteArrayComparable
通過比較器可以實現多樣化目標匹配效果,比較器有以下子類可以使用:
BinaryComparator 匹配完整字節數組
BinaryPrefixComparator 匹配字節數組前綴
BitComparator
NullComparator
RegexStringComparator 正則表達式匹配
SubstringComparator 子串匹配

1. 結構(Structural)過濾器--FilterList
FilterList 代表一個過濾器鏈,它可以包含一組即將應用於目標數據集的過濾器,過濾器間具有“與” FilterList.Operator.MUST_PASS_ALL 和“或” FilterList.Operator.MUST_PASS_ONE 關系。

官網實例代碼,兩個“或”關系的過濾器的寫法:
FilterList list = new FilterList(FilterList.Operator.MUST_PASS_ONE); //數據只要滿足一組過濾器中的一個就可以
SingleColumnValueFilter filter1 = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
Bytes.toBytes("my value")
);
list.add(filter1);
SingleColumnValueFilter filter2 = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
Bytes.toBytes("my other value")
);
list.add(filter2);
Scan scan = new Scan();
scan.setFilter(list);
2. 列值過濾器--SingleColumnValueFilter
SingleColumnValueFilter 用於測試列值相等 (CompareOp.EQUAL ), 不等 (CompareOp.NOT_EQUAL),或單側範圍 (e.g., CompareOp.GREATER)。
構造函數:
(1)比較的關鍵字是一個字符數組
SingleColumnValueFilter(byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value)
(2)比較的關鍵字是一個比較器(比較器下一小節做介紹)
SingleColumnValueFilter(byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, ByteArrayComparable comparator)
2.1.第一種構造函數情況 -- 比較的關鍵字是字符數組
官網示例代碼,檢查列值和字符串‘my value‘ 相等:
SingleColumnValueFilter filter = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
Bytes.toBytes("my value")
);
scan.setFilter(filter);

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
SingleColumnValueFilter filter = new SingleColumnValueFilter(
Bytes.toBytes("patentinfo"),
Bytes.toBytes("CREATE_TIME"),
CompareOp.EQUAL,
Bytes.toBytes("2013-06-08")
);
filterList.addFilter(filter);
Scan scan = new Scan();
scan.setFilter(filterList);
ResultScanner rs = table.getScanner(scan);
for (Result r : rs) {
System.out.println("Scan: " + r);
}
table.close();
註意:還是大寫問題,HBase的列名必須大寫!

2.2.第二種構造函數情況 -- 比較的關鍵字是比較器ByteArrayComparable
該章節主要是針對SingleColumnValueFilter的第二種構造函數使用情況做了一些舉例:
(1)支持值比較的正則表達式 -- RegexStringComparator
官網示例代碼:
RegexStringComparator comp = new RegexStringComparator("my."); //任意以my打頭的值
SingleColumnValueFilter filter = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
comp
);
scan.setFilter(filter);

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);

RegexStringComparator comp = new RegexStringComparator("2013-06-1.");

SingleColumnValueFilter filter = new SingleColumnValueFilter(
Bytes.toBytes("patentinfo"),
Bytes.toBytes("CREATE_TIME"),
CompareOp.EQUAL,
comp
);
filterList.addFilter(filter);
Scan scan = new Scan();
scan.setFilter(filterList);
ResultScanner rs = table.getScanner(scan);
for (Result r : rs) {
System.out.println("Scan: " + r);
}
table.close();

(2)檢測一個子串是否存在於值中(大小寫不敏感) -- SubstringComparator
官網示例代碼:
SubstringComparator comp = new SubstringComparator("y val"); // looking for ‘my value‘
SingleColumnValueFilter filter = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
comp
);
scan.setFilter(filter);

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);

// RegexStringComparator comp = new RegexStringComparator("2013-06-1.");
SubstringComparator comp = new SubstringComparator("2013-06-1");

SingleColumnValueFilter filter = new SingleColumnValueFilter(
Bytes.toBytes("patentinfo"),
Bytes.toBytes("CREATE_TIME"),
CompareOp.EQUAL,
comp
);
filterList.addFilter(filter);
Scan scan = new Scan();
scan.setFilter(filterList);
ResultScanner rs = table.getScanner(scan);
for (Result r : rs) {
System.out.println("Scan: " + r);
}
table.close();

(3)BinaryComparator
二進制比較器,用得較少,有需要請自行查閱官網:http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/BinaryComparator.html

(4)BinaryPrefixComparator
二進制前綴比較器,用得較少,有需要請自行查閱官網:http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/BinaryPrefixComparator.html
3. 鍵值元數據
由於HBase 采用鍵值對保存內部數據,鍵值元數據過濾器評估一行的鍵(ColumnFamily:Qualifiers)是否存在 , 對應前節所述值的情況。
3.1. 基於列族過濾數據的FamilyFilter
構造函數:
FamilyFilter(CompareFilter.CompareOp familyCompareOp, ByteArrayComparable familyComparator)

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
/**
* FamilyFilter構造函數中第二個參數是ByteArrayComparable類型
* ByteArrayComparable類參見“引言-參數基礎”章節
* 下面僅以最可能用到的BinaryComparator、BinaryPrefixComparator舉例:
*/
FamilyFilter ff = new FamilyFilter(
CompareFilter.CompareOp.EQUAL ,
new BinaryComparator(Bytes.toBytes("pat")) //表中不存在pat列族,過濾結果為空
);
FamilyFilter ff1 = new FamilyFilter(
CompareFilter.CompareOp.EQUAL ,
new BinaryPrefixComparator(Bytes.toBytes("pat")) //表中存在以pat打頭的列族patentinfo,過濾結果為該列族所有行
);
Scan scan = new Scan();
scan.setFilter(ff1);
ResultScanner rs = table.getScanner(scan);

註意:
如果希望查找的是一個已知的列族,則使用 scan.addFamily(family) 比使用過濾器效率更高;
由於目前HBase對多列族支持不完善,所以該過濾器目前用途不大。
3.2. 基於限定符Qualifier(列)過濾數據的QualifierFilter
構造函數:
QualifierFilter(CompareFilter.CompareOp op, ByteArrayComparable qualifierComparator)

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
/**
* QualifierFilter構造函數中第二個參數是ByteArrayComparable類型
* ByteArrayComparable類有以下子類可以使用:
* *******************************************
* BinaryComparator 匹配完整字節數組,
* BinaryPrefixComparator 匹配開始的部分字節數組,
* BitComparator,
* NullComparator,
* RegexStringComparator, 正則表達式匹配
* SubstringComparator
* *******************************************
* 下面僅以最可能用到的BinaryComparator、BinaryPrefixComparator舉例:
*/
QualifierFilter ff = new QualifierFilter(
CompareFilter.CompareOp.EQUAL ,
new BinaryComparator(Bytes.toBytes("belong")) //表中不存在belong列,過濾結果為空
);
QualifierFilter ff1 = new QualifierFilter(
CompareFilter.CompareOp.EQUAL ,
new BinaryPrefixComparator(Bytes.toBytes("BELONG")) //表中存在以BELONG打頭的列BELONG_SITE,過濾結果為所有行的該列數據
);
Scan scan = new Scan();
scan.setFilter(ff1);
ResultScanner rs = table.getScanner(scan);
說明:
一旦涉及到列(Qualifier),HBase就只認大寫字母了!
該過濾器應該比FamilyFilter更常用!
3.3. 基於列名(即Qualifier)前綴過濾數據的ColumnPrefixFilter ( 該功能用QualifierFilter也能實現 )
構造函數:
ColumnPrefixFilter(byte[] prefix)

註意:
一個列名是可以出現在多個列族中的,該過濾器將返回所有列族中匹配的列。

官網示例代碼,查找所有"abc"打頭的列:
HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[] prefix = Bytes.toBytes("abc");
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new ColumnPrefixFilter(prefix);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
for (KeyValue kv : r.raw()) {
// each kv represents a column
}
}
rs.close();

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
//返回所有行中以BELONG打頭的列的數據
ColumnPrefixFilter ff1 = new ColumnPrefixFilter(Bytes.toBytes("BELONG"));
Scan scan = new Scan();
scan.setFilter(ff1);
ResultScanner rs = table.getScanner(scan);

3.4. 基於多個列名(即Qualifier)前綴過濾數據的MultipleColumnPrefixFilter
說明:
MultipleColumnPrefixFilter 和 ColumnPrefixFilter 行為差不多,但可以指定多個前綴。

官方示例代碼,查找所有"abc"或"xyz"打頭的列:
HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[][] prefixes = new byte[][] {Bytes.toBytes("abc"), Bytes.toBytes("xyz")};
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new MultipleColumnPrefixFilter(prefixes);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
for (KeyValue kv : r.raw()) {
// each kv represents a column
}
}
rs.close();

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");

byte[][] prefixes = new byte[][] {Bytes.toBytes("BELONG"), Bytes.toBytes("CREATE")};
//返回所有行中以BELONG或者CREATE打頭的列的數據
MultipleColumnPrefixFilter ff = new MultipleColumnPrefixFilter(prefixes);

Scan scan = new Scan();
scan.setFilter(ff);
ResultScanner rs = table.getScanner(scan);

3.5. 基於列範圍(不是行範圍)過濾數據ColumnRangeFilter
說明:
可用於獲得一個範圍的列,例如,如果你的一行中有百萬個列,但是你只希望查看列名為bbbb到dddd的範圍
該方法從 HBase 0.92 版本開始引入
一個列名是可以出現在多個列族中的,該過濾器將返回所有列族中匹配的列

構造函數:
ColumnRangeFilter(byte[] minColumn, boolean minColumnInclusive, byte[] maxColumn, boolean maxColumnInclusive)
參數解釋:
minColumn - 列範圍的最小值,如果為空,則沒有下限;
minColumnInclusive - 列範圍是否包含minColumn ;
maxColumn - 列範圍最大值,如果為空,則沒有上限;
maxColumnInclusive - 列範圍是否包含maxColumn 。

官網示例代碼,查找列名在"bbbb"到"dddd"範圍的數據:
HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[] startColumn = Bytes.toBytes("bbbb");
byte[] endColumn = Bytes.toBytes("bbdd");
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new ColumnRangeFilter(startColumn, true, endColumn, true);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
for (KeyValue kv : r.raw()) {
// each kv represents a column
}
}
rs.close();

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");

byte[] startColumn = Bytes.toBytes("C");
byte[] endColumn = Bytes.toBytes("D");
//返回所有列中從C到D打頭的範圍的數據,實際返回類似CREATOR、CREATE_TIME、CHANNEL_CODE等列的數據
ColumnRangeFilter ff = new ColumnRangeFilter(startColumn, true, endColumn, true);

Scan scan = new Scan();
scan.setFilter(ff);
ResultScanner rs = table.getScanner(scan);
4. RowKey
當需要根據行鍵特征查找一個範圍的行數據時,使用Scan的startRow和stopRow會更高效,但是,startRow和stopRow只能匹配行鍵的開始字符,而不能匹配中間包含的字符:
byte[] startColumn = Bytes.toBytes("aaa");
byte[] endColumn = Bytes.toBytes("bbb");
Scan scan = new Scan(startColumn,endColumn);

當需要針對行鍵進行更復雜的過濾時,可以使用RowFilter:

構造函數:
RowFilter(CompareFilter.CompareOp rowCompareOp, ByteArrayComparable rowComparator)
參數解釋參見“引言-參數基礎”章節。

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
/**
* rowkey格式為:創建日期_發布日期_ID_TITLE
* 目標:查找 發布日期 為 2013-07-16 的數據
*/
RowFilter rf = new RowFilter(
CompareFilter.CompareOp.EQUAL ,
new SubstringComparator("_2013-07-16_")
);
Scan scan = new Scan();
scan.setFilter(rf);
ResultScanner rs = table.getScanner(scan);
註意:
測試過程中嘗試通過組合使用兩個RowFilter(CompareFilter.CompareOp參數分別為GREATER_OR_EQUAL和LESS_OR_EQUAL),和SubstringComparator,過濾找出指定發布時間範圍內的數據,但結果比較意外,不是預想的數據,估計比較運算符GREATER_OR_EQUAL和LESS_OR_EQUAL和比較器SubstringComparator組合使用效果不太好,慎用。
5.PageFilter
指定頁面行數,返回對應行數的結果集。
需要註意的是,該過濾器並不能保證返回的結果行數小於等於指定的頁面行數,因為過濾器是分別作用到各個region server的,它只能保證當前region返回的結果行數不超過指定頁面行數。

構造函數:
PageFilter(long pageSize)

實測代碼(從“2013-07-26”行開始,取5行):
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("2013-07-26"));
PageFilter pf = new PageFilter(5L);
scan.setFilter(pf);
ResultScanner rs = table.getScanner(scan);
for (Result r : rs) {
for (Cell cell : r.rawCells()) {
System.out.println("Rowkey : " + Bytes.toString(r.getRow())
+ " Familiy:Quilifier : "
+ Bytes.toString(CellUtil.cloneQualifier(cell))
+ " Value : "
+ Bytes.toString(CellUtil.cloneValue(cell))
+ " Time : " + cell.getTimestamp());
}
}
註意:
由於該過濾器並不能保證返回的結果行數小於等於指定的頁面行數,所以更好的返回指定行數的辦法是ResultScanner.next(int nbRows) ,即:
ResultScanner rs = table.getScanner(scan);
for (Result r : rs.next(5)) {
for (Cell cell : r.rawCells()) {
System.out.println("Rowkey : " + Bytes.toString(r.getRow())
+ " Familiy:Quilifier : "
+ Bytes.toString(CellUtil.cloneQualifier(cell))
+ " Value : "
+ Bytes.toString(CellUtil.cloneValue(cell))
+ " Time : " + cell.getTimestamp());
}
}

6.SkipFilter
根據整行中的每個列來做過濾,只要存在一列不滿足條件,整行都被過濾掉。
例如,如果一行中的所有列代表的是不同物品的重量,則真實場景下這些數值都必須大於零,我們希望將那些包含任意列值為0的行都過濾掉。
在這個情況下,我們結合ValueFilter和SkipFilter共同實現該目的:
scan.setFilter(new SkipFilter(new ValueFilter(CompareOp.NOT_EQUAL,new BinaryComparator(Bytes.toBytes(0))));

構造函數:
SkipFilter(Filter filter)

個人實測代碼:
目前的數據:
hbase(main):009:0> scan ‘rd_ns:itable‘
ROW COLUMN+CELL
100001 column=info:address, timestamp=1405417403438, value=anywhere
100001 column=info:age, timestamp=1405417403438, value=24
100001 column=info:name, timestamp=1405417403438, value=zhangtao
100002 column=info:address, timestamp=1405417426693, value=shangdi
100002 column=info:age, timestamp=1405417426693, value=28
100002 column=info:name, timestamp=1405417426693, value=shichao
100003 column=info:address, timestamp=1405494141522, value=huilongguan
100003 column=info:age, timestamp=1405494999631, value=29
100003 column=info:name, timestamp=1405494141522, value=liyang
3 row(s) in 0.0190 seconds

執行以下代碼:
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "rd_ns:itable");
Scan scan = new Scan();
scan.setFilter(new SkipFilter(new ValueFilter(CompareOp.NOT_EQUAL,
new BinaryComparator(Bytes.toBytes("28")))));
ResultScanner rs = table.getScanner(scan);
for (Result r : rs) {
for (Cell cell : r.rawCells()) {
System.out.println("Rowkey : " + Bytes.toString(r.getRow())
+ " Familiy:Quilifier : "
+ Bytes.toString(CellUtil.cloneQualifier(cell))
+ " Value : "
+ Bytes.toString(CellUtil.cloneValue(cell))
+ " Time : " + cell.getTimestamp());
}
}
table.close();
輸出結果(整個100002行被過濾掉了):
Rowkey : 100001 Familiy:Quilifier : address Value : anywhere Time : 1405417403438
Rowkey : 100001 Familiy:Quilifier : age Value : 24 Time : 1405417403438
Rowkey : 100001 Familiy:Quilifier : name Value : zhangtao Time : 1405417403438
Rowkey : 100003 Familiy:Quilifier : address Value : huilongguan Time : 1405494141522
Rowkey : 100003 Familiy:Quilifier : age Value : 29 Time : 1405494999631
Rowkey : 100003 Familiy:Quilifier : name Value : liyang Time : 1405494141522
7. Utility--FirstKeyOnlyFilter
該過濾器僅僅返回每一行中的第一個cell的值,可以用於高效的執行行數統計操作。
估計實戰意義不大。

構造函數:
public FirstKeyOnlyFilter()

個人實測代碼:
HTable table = HBaseDAO.getHTable("147patents");
FirstKeyOnlyFilter fkof = new FirstKeyOnlyFilter();
Scan scan = new Scan();
scan.setFilter(fkof);
ResultScanner rs = table.getScanner(scan);

8. 取得查詢結果
無論是官網的Ref Guide還是網上流傳的大部分博客中,輸出查詢結果的代碼都是:
for (Result r = rs.next(); r != null; r = rs.next()) {
for (KeyValue kv : r.raw()) {
// each kv represents a column
}
}

但查看最新的API可知Result實例的raw()方法已經不建議使用了:
raw() Deprecated. as of 0.96, use rawCells()

0.96以後版本正確的獲取結果代碼如下:
for (Result r : rs) {
for (Cell cell : r.rawCells()) {
System.out.println(
"Rowkey : "+Bytes.toString(r.getRow())+
"Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+
"Value : "+Bytes.toString(CellUtil.cloneValue(cell))
);
}
}
---------------------
作者:首席撩妹指導官
來源:CSDN
原文:https://blog.csdn.net/qq_36864672/article/details/78624856?utm_source=copy
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

HBase - Filter - 過濾器的介紹以及使用