HBase行锁RowLock源码分析(3)

转载 2013年12月06日 10:19:46

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 API however they are discouraged because if not managed properly these can lock up the RegionServers. There is an oustanding ticket HBASE-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中应用了各种各样高效的可重入锁,包括行级别的rowlock、mvcc,region级别的读写锁,store级别的读写锁,memstore级别的读写锁等等...

HBase RowLock系列问题

Hbase RowLock UnknownRowLockException

HBase1.1.2 row lock

版本:HBase:1.1.2;Hadoop2.6.0;Eclipse:Mars.1 Release (4.5.1);应用场景:HBase中有一个表,里面有一条记录,如下:现在需要根据value的值,比...

总结一下HBase各种级别的锁以及对读写的阻塞

为了保证并发操作时数据的一致性和性能,HBase中应用了各种各样高效的可重入锁,包括行级别的rowlock、mvcc,region级别的读写锁,store级别的读写锁,memstore级别的读写锁等等...

PHP中Ajax使用总结

Ajax的全称是“Asynchronous JavaScript And XML”,它不是一门新语言,而是一种新技术,它可以创建更好、更快及交互性更强的Web应用程序。Ajax使用了JavaScrip...

HBase行锁RowLock源码分析(2)

HBase(0.89.20100726)中的RowLock使用及实现  HBase只实现了基于row-key的锁机制。  1.客户端代码    RowLock rl = table.lock...
  • xtjsxtj
  • xtjsxtj
  • 2013年12月06日 10:18
  • 1064

有关数据库 行 锁 的几个问题(rowlock) 行锁的基本说明: SELECT au_lname FROM authors WITH (NOLOCK) 锁定提示

有关数据库 行 锁 的几个问题(rowlock) 行锁的基本说明: SELECT au_lname FROM authors WITH (NOLOCK) 锁定提示    ...

有关数据库 行 锁 的几个问题(rowlock)

有关数据库 行 锁 的几个问题(rowlock) 行锁的基本说明: SELECT au_lname FROM authors WITH (NOLOCK) 锁定提示  ...

Hbase-0.98.6源码分析--行锁

在传统的关系型数据库,如MySQL中,存在事务(Transaction)这一概念 ,事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的...

Hbase源码研究(五)------put(3)

继续上面的分析,主要看提交这下 OperationStatus[] codes = region.put(putsWithLocks.toArray(new Pair[]{})); 仔细看put...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HBase行锁RowLock源码分析(3)
举报原因:
原因补充:

(最多只允许输入30个字)