HBase 计数器 (Counters)

本文介绍了HBase中的计数器功能,包括计数器的基本使用、初始化计数器的方法、单个计数器和多个计数器的操作方式。通过示例展示了如何使用HBase shell及客户端API对计数器进行增减操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值