计数器一般用于实时统计(点击流或者在线广告),而不是使用高延迟的批量处理
计数器与原子操作检查并修改一样,这种机制可以将列当做计数器。而不用使用对一行加锁、修改、释放行锁这种容易引起大量资源竞争的操作(如果客户端进程崩溃的话,未释放的锁就会等待超时恢复、在高负载的系统中这种后果很严重)
客户端API提供计数器来完成原子操作检查并修改,保证客户端调用的原子性,多次调用可以使用一次RPC请求
客户端可以一次更新多个计数器但是它们必须是同一行,更新多个计数器需通过独立的API调用,即多个RPC请求
//终端的incr命令格式
incr 'table','row','column',[increment-value]
//计数器在列级的细节,每次调用命令返回这个计数器的新值,最后get_counter显示当前计数器值
//daily weekly monthly是列族
hbase(main):001:0> create 'counter','daily','weekly','monthly'
0 row(s) in 1.6090 seconds
=> Hbase::Table - counter
hbase(main):002:0> incr 'counter','20180128','daily:hits',1
COUNTER VALUE = 1
0 row(s) in 0.2970 seconds
hbase(main):003:0> incr 'counter','20180128','daily:hits',1
COUNTER VALUE = 2
0 row(s) in 0.0140 seconds
hbase(main):004:0> get_counter 'counter','20180128','daily:hits'
COUNTER VALUE = 2
用户不用初始化计数器,当列被创建时,计数器的值就被初始化为0。操作时就会返回1或者设定值
//直接读写一个计数器,需要解码
Bytes.toLong()
//编码
Bytes.toBytes(long)//长整型
//类型转换
byte[] b1 = Bytes.toBytes(1L)
byte[] b1 = Bytes.toBytes((long)var)
//也可以使用get访问这个计数器,或者增加更大的设定值
hbase(main):008:0> get 'counter','20180128'
daily:hits timestamp=1517130705655, value=\x00\x00\x00\x00\x00\x00\x00\x02
//shell把每一个字节按16进制数打印,get_counter 可以以可读形式读取值
//增加值可以是
0 得到计数器当前值
大于零 增加计数器值
小于零 减少计数器值
//incr一次只能操作一个计数器
单计数器
只能操作一个计数器:需要设定列,方法由HTable提供
long incrementColumnValue(byte[] row,byte[] family,byte[] qualifier, long amount) throws IOException
long incrementColumnValue(byte[] row,byte[] family,byte[] qualifier, long amount,boolean writeToWAL) throws IOException
两种方法需要提供列的坐标(coordinates)和增加值
参 数 writeToWAL作用与Put.setWriteToWAL()方法一致。忽略参数直接使用默认值true ,也就是说,WAL是有效的
Configuration hbase = HBaseConfiguration.create();
String tablename = "counter";
HTable table = new HTable(hbase,tablename);
//increment data counter
table.incrementColumnValue(Bytes.toBytes("2018025"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
table.incrementColumnValue(Bytes.toBytes("2018025"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
//0 == get_counter
long cnt = table.incrementColumnValue(Bytes.toBytes("2018025"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 0);
table.incrementColumnValue(Bytes.toBytes("2018025"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), -1);
table.incrementColumnValue(Bytes.toBytes("2018025"), Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
System.out.println("current counter value:"+cnt);
使用正值时增加了计数器的 值,使用0 时可以得到当前计数器的值,使用负值时可以减少当前计数器的值
多计数器
HTablef的方法increment ()
Result increment(Increment increment) throws IOException
需要创建一个Increment实例,同时填充行键,此行应包含此实例需要通过increment () 方法修改的所有计数器
Increment(byte[] row)
Increment(byte[] row,RowLock rowLock)
//rowLock锁实例,使本次操作完全在用户的控制下完成,例如,当用户需要多次修改同一行时,可以保证其间此行不被其他写程 序修改。
用户可以限制其他写程序修改此行的值,但是用户无法限制读操作。事实上,这里并没有保证读操作的原子性.因为读操作不需要获取锁,所以它可能读到一行中被修改到一半的数 据 ! scan和 get操作同样会出现这种情况
使用行键创建了一个Increment实例后,就需要向其中加入实际的计数器,也就是需要增加列
Increment addColumn(byte[] family,byte[] qualifier,long amount)
Increment类的特别功能是可以添加一个时间范围:
Increment setTimeRange(long minStamp,long maxStamp)
时间范围被送到服务器端来限制内部的get操作来取得当前这些计数器的值。用户可以使用它来使计数器过期(expire),例如,用时间划分一行的计数器:用户限制时间范围,可以用来屏蔽比较老的计数器,使它们看上去不存在。一次增加操作会认为这此较老的计数器不存在,并把它们重置为1
Increment类还提供了其他的一些预定于方法
nuraFarailies() 便捷地取回FamilyMap的大小,其中包括添加的所有列的列族
numColumns() 返回将要被处理的列的数目
hasFamilies () 检査是否有列或列族被添加到这个Increment实例中
Configuration hbase = HBaseConfiguration.create();
String tablename = "counter";
HTable table = new HTable(hbase,tablename);
Increment icr = new Increment(Bytes.toBytes("2018028"));
icr.addColumn(Bytes.toBytes("daily"), Bytes.toBytes("hits"), 1);
icr.addColumn(Bytes.toBytes("weekly"), Bytes.toBytes("hits"), 0);
Result result = table.increment(icr);
for(KeyValue kv : result.raw()){
System.out.println("kv data:"+kv);
System.out.println(Bytes.toLong(kv.getValue()));
}
table.close();
一次increment调用只能操纵一行的计数器