HBase行锁
1 行锁简介
在事务特性方面,hbase只支持单row的事务,不能保证跨row(cross-row)的事务。hbase通过行锁来实现单row事务。客户端进行操作时,可以显式对某一个行加锁,但是大部分情况下是没有必要的,因为如果没有显式的加行锁,hbase在内部会默认的加行锁。为了描述方便,用户显式加的行锁简称为显式行锁。
源码1:客户端使用显式行锁的示例代码
HTable table = new HTable(config, tablename);
RowLock lock = table.lockRow(rowkey);//首先获得rowkey对应的行锁,可能阻塞
Put p1 = new Put(rowkey,lock);
p1.add(cf, qua,value); //在行rowkey上,写数据
table.put(p1);
table.unlockRow(lock);//解锁
hbase不推荐客户端显式加行锁,因为可能造成regionserver死锁。并且现在正就"客户端不支持显式加行锁"进行讨论。
引自hbase官网:
RowLocks are still in the client APIhowever they are
discouraged because if not managed properly thesecan lock up the RegionServers. There is an oustanding ticketHBASE-2332
to remove this feature from the client.
2 实现行锁
如下图所示,客户端看到的行锁由两部分组成:rowkey和外部锁id。外部锁id是向regionserver申请行锁时,由regionserver返回的。外部锁id在对应的regionserver上全局唯一。

图1 行锁的定义
如图2所示。一个regionserver对应一个HRegionServer实例,一个分片对应一个HRegion实例。
在HregionServer中提供两个接口lockRow和unlockRow,供客户端显式加锁和解锁。rowlocks存储了<客户端所见的锁id,内部锁id>的映射关系。因为内部锁id在分片内部是唯一的,但是在regionserver上不唯一,所以需要这个映射关系。
在HRegion中,lockedRows记录了所有已经加锁的行,lockIds记录了映射关系<内部锁id,rowkey>。

图2
为了处理客户端忘记释放锁的情况,显式行锁有一个租约(即有效期,默认是60秒,可配置,对应的配置项是hbase.regionserver.lease.period),如果显式行锁超过了租约时间,regionserver会自动释放行锁。在租约时间内,如果又收到了在这个行上的写请求,会延长租约时长。
加显式行锁的算法
1 判断目标行是否已经加锁,如果已经加锁,则等待至解锁。
while (lockedRows.contains(row))
lockedRows.wait();
2 分配内部锁id,记为lockId。并把映射关系<lockIds,row>加入至lockIds。lockId在region内是唯一的。
保证内部锁id唯一性的常规的解决办法是:找出一个没有使用过的id,然后分配给行锁。hbase采用的方法比较有意思:在内部维护一个id生成器,每次分配时,把id生成器的值分配给行锁,并且id生成器自增1。如果没有出现冲突(冲突:当前分配的id不唯一),分配完成。 如果出现冲突,为id生成器赋一随机值,然后重复的上面的流程,直到没有冲突,见源码2。
方法对比:如果使用常规方法,必然要使用到锁来进行并发控制,锁的开销还是比较大的。hbase认为出现冲突的可能性非常小,这种方法的开销应该远小于常规方法。这种提供性能的方法值得学习。
源码2:分配内部锁id。 见HRegion. internalObtainRowLock()
byte[] prev = null;
Integer lockId = null;
do
{
lockId = new Integer(lockIdGenerator++);//生成锁id
prev = lockIds.put(lockId, row);
if (prev != null)
//如果lockId已经分配了,即发生冲突
{
lockIds.put(lockId, prev);// 恢复被破坏的映射关系
lockIdGenerator = rand.nextInt();//生成新的锁id
}
} while (prev != null);
源码:添加映射关系
lockIds.put(lockId, row)
3 把目标行加入至lockedRows。
lockedRows.add(row);
4 为行锁分配外部锁id,这个id在regionserver内是唯一的。
lockId = rand.nextLong();//这种方法会给hbase带来问题,见第三节
5 在行锁上添加行锁监听器。
行锁监听器的主要功能是处理行锁的租约,租约过期时,自动释放行锁,收到新的写请求延长租约时长。
下图是实现行锁的类图。

图3 实现行锁类图
3 设计缺陷
在加显式行锁算法的第4步中,因为Random对象可能产生重复的值,所以分配的外部锁id可能不唯一。
通过修改regionserver的代码,模拟生成重复锁id的情况,测试流程如下:
1修改regionserver的代码使得每次分配的外部锁id都为999
2线程1为rowkey1申请行锁;线程2为rowkey2申请行锁。线程1和线程2同时并发运行。
测试发现只要只要rowkey1对应的行锁不释放,线程2会永远阻塞。
修正方法:保证外部锁id的唯一性。
HBase行锁的更多相关文章
- HBase行锁原理及实现
请带着例如以下问题阅读本文. 1.什么是行锁? 2.HBase行锁的原理是什么? 3.HBase行锁是怎样实现的? 4.HBase行锁是怎样应用的? 一.什么是行锁? 我们知道.数据库中存在事务的概念 ...
- 数据库进阶之路(五) - MySQL行锁深入研究
由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图书借阅系统:假设id=1的这本书库存为1,但是有2个人同时来借这本书,此处的逻辑为: ; --如果restnum大于0,执行u ...
- 浅谈SQL Transaction在请求中断后的行锁表锁
最近在维护Web Service接口时,由于数据数据量达到千万级别,接口调用不时出现错误让人不胜烦恼,经过性能测试查出瓶颈在数据库数据处理上,可着实忙了一番.相信众多程序猿和DBA都会头痛性能的问题, ...
- MySQL行锁深入研究
原文:http://blog.csdn.net/minipeach/article/details/5325161/ 做项目时由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图 ...
- select for update行锁
select for update行锁 2008-05-26 15:15:37 分类: Oracle Select-For Update语句的语法与select语句相同,只是在select语句的后面 ...
- Innodb行锁源码学习(一)
Innodb是mysql数据库中目前最流行的存储引擎,innodb相对其它存储引擎一个很大的特点是支持事务,并且支持行粒度的锁.今天我重点跟大家分享下innodb行锁实现的基础知识.由于篇幅比较大,文 ...
- mysql行锁和表锁
mysql innodb支持行锁和表锁,但是MyIsam只支持表锁.现在我们说说mysql innodb的行锁和 有如下表id为主键 为了出现演示效果,我们将mysql的autocommit设置为0 ...
- Mysql InnoDB行锁实现方式(转)
Mysql InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点 ...
- Mysql InnoDB行锁实现方式
Mysql InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点 ...
随机推荐
- Erlang application stop 调用死锁
Erlang application stop 调用死锁(金庆的专栏)在application行为模块的start()中启动bson应用,在stop()中停止bson,结果application:st ...
- CSDN帐号被封
感慨
- 03_MyBatis基本查询,mapper文件的定义,测试代码的编写,resultMap配置返回值,sql片段配置,select标签标签中的内容介绍,配置使用二级缓存,使用别名的数据类型,条件查询ma
1 PersonTestMapper.xml中的内容如下: <?xmlversion="1.0"encoding="UTF-8"?> < ...
- android 使用Vysor投影到电脑
有没有好的投影软件可以将android屏幕投影到电脑,当然这种很多,比如360就自带了投影功能,小米盒子也可以(不过貌似只能支持到4.4版本),今天要说的是Vysor,google的一款投影软件. V ...
- AndroidStudio如何快速制作.so
之前写过一篇Eclipse制作.so的文章,http://blog.csdn.net/baiyuliang2013/article/details/44306921使用的是GNUstep模拟Linux ...
- Sqoop执行mysql删除语句
如果使用Sqoop删除mysql中的数据,并且传递动态日期参数,则使用下方的方法: 创建一个sh文件,内容如下: #!/bin/sh ## 环境变量生效 . /etc/profile #[调度删除导入 ...
- UNIX网络编程——shutdown 与 close 函数 的区别
假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...
- UNIX网络编程——僵尸进程
在fork()/exec()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用waitpid()等待子进程结束,又没有显式忽略该信号,则子进 ...
- Mybatis源码之Statement处理器PreparedStatementHandler(五)
PreparedStatementHandler就是调用PreparedStatement来执行SQL语句,这样在第一次执行sql语句时会进行预编译,在接下来执行相同的SQL语句时会提高数据库性能 源 ...
- 1020. Tree Traversals (25) -BFS
题目如下: Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder ...