Hbase api 中 put delete checkandput 等都是独立执行的。
行锁分为隐式和显式。
行锁保证了只有一个客户端能获取一行数据的锁。
显式应用行锁已经被作为过时,不推荐使用。
行锁会不会出现死锁?
两个客户端拥有对方要的行锁,又同时请求对方拥有的锁,必然死锁。
这时,就需要一个服务器端额处理线程,来控制锁超时解决死锁的问题。
由hbase.regionserver.lease.period 属性设置超时时间。(后面看到这个配置也是控制扫描器租约的时间,用来保护其不被失效的客户端阻塞太久。前提是scanner 没有合理的close。)
public class RowLockTest implements Runnable {
private static Configuration conf;
private static HConnection conn;
private static HTableInterface table;
public static void init() throws IOException {
conf = HBaseConfiguration.create();
conn = HConnectionManager.createConnection(conf);
table = conn.getTable("students".getBytes());
}
@Override
public void run() {
try {
System.out.println("Thread trying to put same row now ...");
Configuration conf1 = HBaseConfiguration.create();
HConnection conn1 = HConnectionManager.createConnection(conf1);
HTableInterface table1 = conn1.getTable("students".getBytes());
Put put = new Put("Howard".getBytes());
put.add("moreInfo".getBytes(), "phone".getBytes(), "110".getBytes());
long time = System.currentTimeMillis();
table1.put(put);
System.out.println("Wait time: "
+ (System.currentTimeMillis() - time) + " ms");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
init();
System.out.println("Taking out lock...");
RowLock lock = table.lockRow("Howard".getBytes());
System.out.println("Lock ID: " + lock.getLockId());
Thread thread = new Thread(new RowLockTest());
thread.start();
try {
System.out.println("Sleeping 2s in main()...");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Put put1 = new Put("Howard".getBytes());
put1.add("basicInfo".getBytes(), "age".getBytes(), "19".getBytes());
table.put(put1);
Put put2 = new Put("Howard".getBytes());
put2.add("moreInfo".getBytes(), "phone".getBytes(),
"119".getBytes());
table.put(put2);
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println("Releasing lock...");
table.unlockRow(lock);
}
}
}
如上,执行时,报出异常 UnknownRowLockException,原因是 在占有锁的线程main中,put构造器要把锁作为参数传给它,才能正确执行,不然regionserver不知道你占用的锁是哪个,报出异常,有兴趣的可以跟踪以下源码,查看具体执行机制。
有意思的是,上述代码中,如果run()中没有重新创建HConnection,而是用类中的static配置的几个conf、conn,会抛出异常,不能创建session等,可以理解为两个线程不能共享一个HConnection,需要有各自的连接。
修改过后,执行成功。
Taking out lock...
Lock ID: -3305589738841484909
Sleeping 2s in main()...
Thread trying to put same row now ...
11:28:33,075 INFO RecoverableZooKeeper:103 - The identifier of this process is 6516@LS--20151016UGR
11:28:33,076 INFO ZooKeeper:438 - Initiating client connection, connectString=hadoop-master:2181 sessionTimeout=180000 watcher=hconnection0x0
11:28:33,081 INFO ClientCnxn:966 - Opening socket connection to server hadoop-master/192.168.159.128:2181. Will not attempt to authenticate using SASL (unknown error)
11:28:33,083 INFO ClientCnxn:849 - Socket connection established to hadoop-master/192.168.159.128:2181, initiating session
11:28:33,905 INFO ClientCnxn:1207 - Session establishment complete on server hadoop-master/192.168.159.128:2181, sessionid = 0x158b7fc11490024, negotiated timeout = 180000
Releasing lock...
Wait time: 2665 ms