HBase 計數器 (Counters)

HBase 提供了一個高級特性:計數器(counter)。很多收集統計信息的應用,例如在線廣告的單擊或查看統計,將這些數據收集到日誌文件中用於後期的分析。
利用計數器提供的實時統計,從而放棄延時較高的批處理操作。


2.1 計數器簡介 (Introduction to Counters)
-----------------------------------------------------------------------------------------------------------------------------------------
看看計數器在列級別的工作。下面是一個 shell 示例,創建表,增加計數器 2 次,然後查詢當前值。

    hbase(main):001:0> create 'counters', 'daily', 'weekly', 'monthly'
    0 row(s) in 1.1930 seconds

    hbase(main):002:0> incr 'counters', '20150101', 'daily:hits', 1
    COUNTER VALUE = 1
    0 row(s) in 0.0490 seconds

    hbase(main):003:0> incr 'counters', '20150101', 'daily:hits', 1
    COUNTER VALUE = 2
    0 row(s) in 0.0170 seconds

    hbase(main):04:0> get_counter 'counters', '20150101', 'daily:hits'
    COUNTER VALUE = 2

每次調用 incr 增加計時器給定的值,這裏爲 1。最後使用 get_counter 得到如期的當前值。
shell 的 incr 命令格式如下:

    incr '<table>', '<row>', '<column>', [<increment-value>]

    [<increment-value>] 爲可選項,如果忽略,增量值默認爲 1    

    
    ● 初始化計數器 (Initializing Counters)
    -------------------------------------------------------------------------------------------------------------------------------------
    用戶不需要初始化計數器,第一次使用一個新的計時器時,計時器被自動設爲 0,也就是說,列限定符還不存在時,計數器的值爲 0。第一次增加操作
    會將計數器值設爲 1 或者設定的值。也可以直接讀取或寫入一個計數器的值,但必須使用以下方法解碼爲值:
    
        Bytes.toLong()
        
    並且使用
    
        Bytes.toBytes(long)

    對存儲的值進行編碼。特別是後一種情況,在調用 toBytes() 方法時,必須保證使用長整型的(long)的數字。也可以考慮對變量或數字進行類型轉換來
    顯式使用 long 類型,例如:
    
        byte[] b1 = Bytes.toBytes(1L)
        byte[] b2 = Bytes.toBytes((long) var)
    
可以使用 get 調用來訪問計時器:

    hbase(main):005:0> get 'counters', '20150101'
    COLUMN CELL
    daily:hits timestamp=1427485256567, value=\x00\x00\x00\x00\x00\x00\x00\x02
    1 row(s) in 0.0280 seconds    

這樣的結果可讀性很差,但這表明了一個計數器就是一個與其它列類似的簡單列。

也可以指定一個更大的低增值:

    hbase(main):006:0> incr 'counters', '20150101', 'daily:hits', 20
    COUNTER VALUE = 22
    0 row(s) in 0.0180 seconds

    hbase(main):007:0> get_counter 'counters', '20150101', 'daily:hits'
    COUNTER VALUE = 22

    hbase(main):008:0> get 'counters', '20150101'
    COLUMN CELL
    daily:hits timestamp=1427489182419, value=\x00\x00\x00\x00\x00\x00\x00\x16
    1 row(s) in 0.0200 seconds

通過 get 直接訪問計時器值得到的是字節數組,shell 把每個字節按十六進制數打印。使用 get_counter 可以獲得可讀格式的返回數據。

可以使用 incr 命令來對計時器增加值,也可取回計時器當前值或者減少當前值。

    hbase(main):009:0> incr 'counters', '20150101', 'daily:hits'
    COUNTER VALUE = 23
    0 row(s) in 0.1700 seconds
    
    hbase(main):010:0> incr 'counters', '20150101', 'daily:hits'
    COUNTER VALUE = 24
    0 row(s) in 0.0230 seconds
    
    hbase(main):011:0> incr 'counters', '20150101', 'daily:hits', 0
    COUNTER VALUE = 24
    0 row(s) in 0.0170 seconds
    
    hbase(main):012:0> incr 'counters', '20150101', 'daily:hits', -1
    COUNTER VALUE = 23
    0 row(s) in 0.0210 seconds
    
    hbase(main):013:0> incr 'counters', '20150101', 'daily:hits', -1
    COUNTER VALUE = 22
    0 row(s) in 0.0200 seconds

利用 incr 命令的最後一個參數,增量值,可以實現下表中所示的行爲:

    The increment value and its effect on counter increments
    +-------------------+-----------------------------------------------------------------------------------------
    | Value                | Effect
    +-------------------+-----------------------------------------------------------------------------------------
    | greater than zero    | Increase the counter by the given value.
    +-------------------+-----------------------------------------------------------------------------------------
    | zero                | Retrieve the current value of the counter. Same as using the get_counter shell command.
    +-------------------+-----------------------------------------------------------------------------------------
    | less than zero    | Decrease the counter by the given value
    +-------------------+-----------------------------------------------------------------------------------------

顯然,使用 incr 命令只能一次操作一個計時器。也可以使用後面介紹的客戶端 API 來操作計數器。


2.2 單一計數器 (Single Counters)
-----------------------------------------------------------------------------------------------------------------------------------------
第一種增加調用是隻對單一計數器的:需要精確指定要使用的列(column)。方法由 Table 提供:

    long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount) throws IOException;
    long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount, Durability durability) throws IOException;

給出列的座標,以及增加值,這兩個方法區別就在於可選的 durability 參數,與 Put.setDurability() 同樣的方式工作。忽略 durability 參數使用默認
的 Durability.SYNC_WAL 方式工作,意思是使用 write-ahead log.

示例: Example using the single counter increment methods

    long cnt1 = table.incrementColumnValue(Bytes.toBytes("20110101"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
    long cnt2 = table.incrementColumnValue(Bytes.toBytes("20110101"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
    long current = table.incrementColumnValue(Bytes.toBytes("20110101"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 0);
    long cnt3 = table.incrementColumnValue(Bytes.toBytes("20110101"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), -1);

輸出:
    cnt1: 1, cnt2: 2, current: 2, cnt3: 1
    

2.3 多計數器 (Multiple Counters)
-----------------------------------------------------------------------------------------------------------------------------------------
另一個增加計數器值的方法是通過 Table 的 increment() 調用。它的工作方式類似於 CRUD 操作,使用如下方法:

    Result increment(final Increment increment) throws IOException

必須創建 Increment 實例,並向其填充適合的細節信息,如計數器的座標。Increment 構造器如下:

    Increment(byte[] row)
    Increment(final byte[] row, final int offset, final int length)
    Increment(Increment i)

創建 Increment 時必須提供一個行鍵(row key), 此行鍵設置了包含後續操作中所有的計數器所在的行。下一個變體構造器接受一個更大的數組,以及偏移量
和長度參數來確定其中的行鍵。最後一個是拷貝構造器,利用已有的實例從中複製所有的狀態信息。

一旦確定了更新計數器所在的行,並創建了 Increment 實例,就需要向其添加實際的計數器,也就是要增加計數器的列(column)。

    Increment addColumn(byte[] family, byte[] qualifier, long amount)
    Increment add(Cell cell) throws IOException

第一個方法接受列座標參數,第二個方法利用已有的 cell. 這是很有用的,如果剛剛已獲取了一個計數器,現在要增加它的值,add() 調用檢查當前給定的
cell 匹配 Increment 實例的行鍵。

這裏不同之處,是與 Put 方法的對比,這裏沒有選項指定一個版本,或者時間戳,在處理增量值的時候,版本是隱式處理的。更有甚者,沒有 addFamily()
方法的等價物,因爲計數器是特定於列的,並且需要明確指定。因此不需要單獨添加列族的方法。

Increment 類的一個特性是具有設置時間範圍的能力:

    Increment setTimeRange(long minStamp, long maxStamp) throws IOException
    TimeRange getTimeRange()

爲一組計數器增量設置時間範圍實際上是隱式處理版本,時間範圍實際上傳遞給服務器以限制其內部的 get 操作,以獲取當前的計數器值。

示例: Example incrementing multiple counters in one row
    
    Increment increment1 = new Increment(Bytes.toBytes("20150101"));
    //Increment the counters with various values
    increment1.addColumn(Bytes.toBytes("daily"), Bytes.toBytes("clicks"), 1);
    increment1.addColumn(Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
    increment1.addColumn(Bytes.toBytes("weekly"), Bytes.toBytes("clicks"), 10);
    increment1.addColumn(Bytes.toBytes("weekly"), Bytes.toBytes("hits"), 10);
    
    //Call the actual increment method with the above counter updates and receive the results
    Result result1 = table.increment(increment1);
    for (Cell cell : result1.rawCells()) {
        //Print the cell and returned counter value.
        System.out.println("Cell: " + cell +
        " Value: " + Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
    }
    
    Increment increment2 = new Increment(Bytes.toBytes("20150101"));
    
    //Use positive, negative, and zero increment values to achieve the wanted counter changes.
    increment2.addColumn(Bytes.toBytes("daily"), Bytes.toBytes("clicks"), 5);
    increment2.addColumn(Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
    increment2.addColumn(Bytes.toBytes("weekly"), Bytes.toBytes("clicks"), 0);
    increment2.addColumn(Bytes.toBytes("weekly"), Bytes.toBytes("hits"), -5);
    
    Result result2 = table.increment(increment2);
    for (Cell cell : result2.rawCells()) {
        System.out.println("Cell: " + cell +
        " Value: " + Bytes.toLong(cell.getValueArray(),
        cell.getValueOffset(), cell.getValueLength()));
    }

    輸出:
    Cell: 20150101/daily:clicks/1427651982538/Put/vlen=8/seqid=0 Value:    1
    Cell: 20150101/daily:hits/1427651982538/Put/vlen=8/seqid=0 Value: 1
    Cell: 20150101/weekly:clicks/1427651982538/Put/vlen=8/seqid=0 Value: 10
    Cell: 20150101/weekly:hits/1427651982538/Put/vlen=8/seqid=0 Value: 10
    Cell: 20150101/daily:clicks/1427651982543/Put/vlen=8/seqid=0 Value: 6
    Cell: 20150101/daily:hits/1427651982543/Put/vlen=8/seqid=0 Value: 2
    Cell: 20150101/weekly:clicks/1427651982543/Put/vlen=8/seqid=0 Value: 10
    Cell: 20150101/weekly:hits/1427651982543/Put/vlen=8/seqid=0 Value: 5

Increment 類提供了許多其他方法,有些方法繼承自其父類,例如 Mutation, 查看 Increment API javadoc 獲取更多信息。

 

參考:

    《HBase - The Definitive Guide - 2nd Edition》Early release —— 2015.7 Lars George

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章