HBase(0.96以上版本)過濾器Filter詳解及例項程式碼

五柳-先生發表於2015-12-19
引言 -- 引數基礎
有兩個引數類在各類Filter中經常出現,統一介紹下:
(1)比較運算子 CompareFilter.CompareOp
比較運算子用於定義比較關係,可以有以下幾類值供選擇:

  1. EQUAL                                  相等
  2. GREATER                              大於
  3. GREATER_OR_EQUAL           大於等於
  4. LESS                                      小於
  5. LESS_OR_EQUAL                  小於等於
  6. NOT_EQUAL                        不等於
(2)比較器  ByteArrayComparable
通過比較器可以實現多樣化目標匹配效果,比較器有以下子類可以使用:
  1. BinaryComparator       
      
         匹配完整位元組陣列 
  2. BinaryPrefixComparator     匹配位元組陣列字首 
  3. BitComparator
  4. NullComparator
  5. RegexStringComparator    正規表示式匹配
  6. 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); 
注意:
  1. 如果希望查詢的是一個已知的列族,則使用 scan.addFamily(family)  比使用過濾器效率更高;
  2. 由於目前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);  

說明:
  1. 一旦涉及到列(Qualifier),HBase就只認大寫字母了!
  2. 該過濾器應該比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
說明:
  1. 可用於獲得一個範圍的列,例如,如果你的一行中有百萬個列,但是你只希望檢視列名為bbbb到dddd的範圍
  2. 該方法從 HBase 0.92 版本開始引入
  3. 一個列名是可以出現在多個列族中的,該過濾器將返回所有列族中匹配的列

建構函式:
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_EQUALLESS_OR_EQUAL),和SubstringComparator,過濾找出指定釋出時間範圍內的資料,但結果比較意外,不是預想的資料,估計比較運算子GREATER_OR_EQUALLESS_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))
                        );
            }

        }  

作者: creditor http://www.xuebuyuan.com/2046843.html


相關文章