HBase行锁RowLock源码分析(1)

转载 2013年12月06日 10:17:57

概论

Google在那篇著名的Bigtable论文中提到,对Bigtable一行的读写是原子的(无论涉及到几个column),这样做的好处就是简化了并发机制,用户也能很容易理解。Bigtable本来也打算实现一个通用的transaction机制,但是最终他们发现大多数用户要的其实只是基于某一行的transaction,所以实现了RowLock,这大大的简化了系统的复杂度,提高了性能。由此可以看出,对用户的需求并不能笼统的接受,要善于发现需求背后的问题,多问一个为什么,那才是真正用户的渴望或者pain point.

由于特定的rowkey只可能属于某一个region server来处理,所以实现这样的锁,并不需要跨机器,只需要在单个JVM中实现,对系统的性能影响有限。

客户端

先从客户端来看看我们怎么用RowLock.

HTable table = new HTable(hbaseConfig, tableName);
//get a row lock from table object
RowLock rl = table.lockRow (“test”.getBytes());
Random r = new Random();

 

List<Put> puts = new ArrayList<Put>();
for (int i = 1; i <= insertCount; ++i) {
byte[] rowkey = new byte[32];
r.nextBytes(rowkey);
r.nextBytes(value);
Put p = new Put(rowkey, rl ); //all the inserting will use the lock
p.add(f1, null, value);
p.add(f2, null, value);
p.add(f3, null, value);
p.add(f4, null, value);
puts.add(p);
if (i % 1000 == 0) {
table.put(puts);
puts.clear();
}
}
table.unlockRow (rl);

这里我们首先调用了HTable的lockRow方法,然后把它传给了Put对象,这样在我们整个插入的过程就能使用我们定义的行锁。
如果没有传给put对象这个锁,put在插入每一行的时候都会生成一个RowLock。
很自然,我们想看看RowLock对象是什么样子的。

//Row Lock Defination
public class RowLock {
private byte [] row = null;
private long lockId = -1L;

 


}

没有任何特别的地方,只是一个Plain Object。含有目标rowid和一个long的lock ID.

服务器端

Row Lock是由具体的Region Server来负责,所以我们从HRegionServer类看起。

public class HRegionServer implements HConstants, HRegionInterface,
HBaseRPCErrorHandler, Runnable, Watcher{
public long lockRow (byte [] regionName, byte [] row)
throws IOException {
//…
try {
HRegion region = getRegion(regionName);
Integer r = region.obtainRowLock(row);
long lockId = addRowLock(r,region);
LOG.debug(“Row lock ” + lockId + ” explicitly acquired by client”);
return lockId;
} catch (Throwable t) {
throw convertThrowableToIOE(cleanup(t,
“Error obtaining row lock (fsOk: ” + this.fsOk + “)”));
}
}
}

这里主要有两个主要的步骤,一个是调用HRegion的obtainRowLock生成这个锁,然后调用addRowLock记录这个锁和region关系。

//obtainRowLock
public class HRegion{
private final Map<Integer, byte []> locksToRows =  new ConcurrentHashMap<Integer, byte []>();
public Integer obtainRowLock(final byte [] row) throws IOException {
checkRow(row);
splitsAndClosesLock.readLock().lock();
try {
if (this.closed.get()) {
throw new NotServingRegionException(“Region ” + this + ” closed”);
}
Integer key = Bytes.mapKey(row);
synchronized (locksToRows) {
while (locksToRows.containsKey(key)) {
try {
locksToRows.wait();
} catch (InterruptedException ie) {
// Empty
}
}
locksToRows.put(key, row);
locksToRows.notifyAll();
return key;
}

} finally {
splitsAndClosesLock.readLock().unlock();
}
}
}

以上主要做法就是使用一个Map:locksToRows 来记录已经加锁的row。如果已经有人拿到这个锁,其他用户就会在此等待。
注意这里的wait的标准用法

synchronized(object){
while(some condition){
object.wait();
}
//get the lock,do something now
}

接着我们看看addRowLock 的实现部分。

//Add row Lock
public class HRegionServer implements HConstants, HRegionInterface,
HBaseRPCErrorHandler, Runnable, Watcher{
Map<String, Integer> rowlocks =
new ConcurrentHashMap<String, Integer>();
private Leases leases ;

 

this.leases = new Leases(
conf.getInt(“hbase.regionserver.lease.period”, 60 * 1000),
this.threadWakeFrequency);

 

protected long addRowLock(Integer r, HRegion region) throws LeaseStillHeldException {
long lockId = -1L;
lockId = rand.nextLong();
String lockName = String.valueOf(lockId);
synchronized(rowlocks) {
rowlocks.put(lockName, r);
}
this.leases.
createLease(lockName, new RowLockListener(lockName, region));

return lockId;
}
}

这里的关键部分是创建一个lease对象,用于过期来执行某些操作。默认的timeout是60s,也就是说这个锁只能维持60s,过期会由RowLockListener来处理,这样就为了防止用户忘记释放这个锁,造成系统hang的问题,当然你可以通过设定hbase.regionserver.lease.period来改变这个值。接下来看看RowkeyListen干了什么

//RowLockListener
private class RowLockListener implements LeaseListener {
private final String lockName;
private final HRegion region;

 

RowLockListener(final String lockName, final HRegion region) {
this.lockName = lockName;
this.region = region;
}

 

public void leaseExpired () {
LOG.info(“Row Lock ” + this.lockName + ” lease expired”);
Integer r = null;
synchronized(rowlocks) {
r = rowlocks.remove(this.lockName);
}
if(r != null) {
region.releaseRowLock(r);
}
}
}

可以看到RowLockListener实现了leaseExpired方法,用来在lease过期的时候来释放这个Row Lock。
以上我们看了一下加锁的实现,至于释放锁,基本上就是一个反向过程,这里就不描述了。


相关文章推荐

HBase行锁RowLock源码分析(3)

1 行锁简介 在事务特性方面,hbase只支持单row的事务,不能保证跨row(cross-row)的事务。hbase通过行锁来实现单row事务。客户端进行操作时,可以显式对某一个行加锁,但是大...
  • xtjsxtj
  • xtjsxtj
  • 2013年12月06日 10:19
  • 1209

HBase行锁RowLock源码分析(2)

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

HBase1.0.0版源码分析之HMaster启动代码分析(1)

本文其实还算不上真正的启动代码解析,本文主要还是从启动流程上分析到startHMaster部分,初次之外本文将就HBase的伪分布式调试方式进行相关的介绍. 我们将源码倒入到Intellij IDE...

有关数据库 行 锁 的几个问题(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 源码分析1 – RPC机制 : 基础

HBase采用了和Hadoop相同的RPC机制,作为它的主要通信手段.这是一个轻量的,不同于Java标准的RMI的一种方式.所以它的实现必须克服一些问题.如: 1)      如何分配RPC角色和通...

Hbase-0.98.6源码分析--行锁

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

Hbase源码研究(一)------put(1)

近来由于工作需要,阅读了一下hbase的源码(0.92.1版),有时间就看一点儿,慢慢完善。有琢磨的不对的地方,还请大家多多指教。 先从入数据开始。 Put方法最终会调用HTable的doPut方...

Hbase源码研究(二)-------get最终转化为scan来处理(1)

昨天和一个互联网公司的leader争执关于get和scan的问题......记得以前粗略看过一次代码,清楚的记得在0.92.1版本里get最终是转化成scan来处理的......就怪自己当时手贱,脑子...

HBase RowLock系列问题

Hbase RowLock UnknownRowLockException
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HBase行锁RowLock源码分析(1)
举报原因:
原因补充:

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