首先hbase有三处与行锁有关:
1. 计数器
用于实时收集信息,原子操作
在Shell下操作
incr 't1','20150226','daily:hits',1-- t1 表名
--20150226 行健
-- daily是列族
--hits 是列,最后是值
get_counter 't1','row_key','daily:hits'
在java中操作
1 2 | hTable.incrementColumnValue(...) hTable.increment(increment) |
2. check
原子插入(compare-and-set)
1 | hTable.checkAndPut(row,family,qualifier,value,put) |
Value为查出来旧值,put中是新值,当修改的时候会再次判断,如果当前记录的此字段值还是旧值则修改,如果不是则返回false。
经多线程验证确实如此,如:当前值是100,多线程一个减10,一个减20,由于是高并发,因此两个线程都先查出了原始值为100,
然后两个checkAndPut语句分别为:
checkAndPut(row,family,qualifier,100,put-90)
checkAndPut(row,family,qualifier,100,put-80)
先执行的发现目标值为100,因此修改为90成功,后执行的发现目标值为90,但希望旧值为100,因此修改为80失败。
原子删除(compare-and-delete)
1 | hTable.checkAndDelete(row,family,qualifier,value,delete) |
3. Lock
http://blog.163.com/liaoxiangui@126/blog/static/7956964020121324644320
https://blog.csdn.net/yfkiss/article/details/36438855
HBase也提供API(lockRow/unlockRow)显示的获取行锁,但不推荐使用。原因是两个客户端很可能在拥有对方请求的锁时,又同时请求对方已拥有的锁,这样便形成了死锁,在锁超时前,两个被阻塞的客户端都会占用一个服务端的处理线程,而服务器线程是非常稀缺的资源。
Phoenix
https://www.cnblogs.com/haoxinyue/p/6750078.html
因为如果phoenix有索引表的话,直接操作hbase,不会更新索引表,因此需要通过phoenix来操作。Hbase中的checkAndPut看起来能满足行锁的需求,查资料,在phoenix中对应的是:ON DUPLICATE KEY
ON DUPLICATE KEY这个语法要到Phoenix 4.9之后才有。这个功能把HBase的Increment和CheckAndPut两个原子操作合在了一起。当Upsert语句被提交到服务端的时候,所要更新的行会被lock住,同时相关的列会被读取,ON DUPLICATE KEY语句会被执行。
upsert into "admin"("rowkey","loginTimes")values('83dd751',5)on duplicate key update "loginTimes" = 10
upsert into "admin"("rowkey","loginTimes")values('83dd751',5)on duplicate key update "loginTimes" = "loginTimes" +1
这两行原本以为是如下含义:
对于rowkey为'83dd751'的记录,如果loginTimes字段为5就将其设置为10
对于rowkey为'83dd751'的记录,如果loginTimes字段为5就将其设置为自身+1
但测试结果让人大跌眼睛:
1. 那个数字5感觉没什么用,不论当前值为多少,都会设置为10(第一条)
2. 既然那个5没什么用,那至少应该能保证原子性吧,即如果来了5个请求,当前值为0的话,每次加1,应该结果值为5才对,不论怎么并发,它都能保证请求是顺序执行,且每次都能用新的值+1.结果并不是。开了4个线程一起跑10次请求,结果很不稳定,一会儿结果不到10,一会又可以达到10以上甚至20.不知道它内部是怎么计算的。
因此,这算是phoenix的bug,暂时不能用这个方案。