HBase原子性保证

HBase提供基于单数据操作的原子性保证

即:对同一行的变更操作(包括针对一列/多列/多column family的操作),要么完全成功,要么完全失败,不会有其他状态

示例:

A客户端针对rowkey=10的行发起操作:dim1:a = 1  dim2:b=1

B客户端针对rowkey=10的行发起操作:dim1:a = 2  dim2:b=2

dim1、dim2为column family, a、b为column

A客户端和B客户端同时发起请求,最终rowkey=10的行各个列的值可能是dim1:a = 1  dim2:b=1,也可能是dim1:a = 2  dim2:b=2

但绝对不会是dim1:a = 1  dim2:b=2

HBase基于行锁来保证单行操作的原子性,可以看下HRegion put的代码(base: HBase 0.94.20)::

org.apache.hadoop.hbase.regionserver.HRegion:

  /**
   * @param put
   * @param lockid
   * @param writeToWAL
   * @throws IOException
   * @deprecated row locks (lockId) held outside the extent of the operation are deprecated.
   */
  public void put(Put put, Integer lockid, boolean writeToWAL)
  throws IOException {
    checkReadOnly();

    // Do a rough check that we have resources to accept a write.  The check is
    // 'rough' in that between the resource check and the call to obtain a
    // read lock, resources may run out.  For now, the thought is that this
    // will be extremely rare; we'll deal with it when it happens.
    checkResources();
    startRegionOperation();
    this.writeRequestsCount.increment();
    this.opMetrics.setWriteRequestCountMetrics(this.writeRequestsCount.get());
    try {
      // We obtain a per-row lock, so other clients will block while one client
      // performs an update. The read lock is released by the client calling
      // #commit or #abort or if the HRegionServer lease on the lock expires.
      // See HRegionServer#RegionListener for how the expire on HRegionServer
      // invokes a HRegion#abort.
      byte [] row = put.getRow();
      // If we did not pass an existing row lock, obtain a new one
      Integer lid = getLock(lockid, row, true);

      try {
        // All edits for the given row (across all column families) must happen atomically.
        internalPut(put, put.getClusterId(), writeToWAL);
      } finally {
        if(lockid == null) releaseRowLock(lid);
      }
    } finally {
      closeRegionOperation();
    }
  }

getLock调用了internalObtainRowLock:

 private Integer internalObtainRowLock(final HashedBytes rowKey, boolean waitForLock)
      throws IOException {
    checkRow(rowKey.getBytes(), "row lock");
    startRegionOperation();
    try {
      CountDownLatch rowLatch = new CountDownLatch(1);

      // loop until we acquire the row lock (unless !waitForLock)
      while (true) {
        CountDownLatch existingLatch = lockedRows.putIfAbsent(rowKey, rowLatch);
        if (existingLatch == null) {
          break;
        } else {
          // row already locked
          if (!waitForLock) {
            return null;
          }
          try {
            if (!existingLatch.await(this.rowLockWaitDuration,
                            TimeUnit.MILLISECONDS)) {
              throw new IOException("Timed out on getting lock for row=" + rowKey);
            }
          } catch (InterruptedException ie) {
            // Empty
          }
        }
      }

      // loop until we generate an unused lock id
      while (true) {
        Integer lockId = lockIdGenerator.incrementAndGet();
        HashedBytes existingRowKey = lockIds.putIfAbsent(lockId, rowKey);
        if (existingRowKey == null) {
          return lockId;
        } else {
          // lockId already in use, jump generator to a new spot
          lockIdGenerator.set(rand.nextInt());
        }
      }
    } finally {
      closeRegionOperation();
    }
  }

HBase行锁的实现细节推荐下:hbase源码解析之行锁

HBase也提供API(lockRow/unlockRow)显示的获取行锁,但不推荐使用。原因是两个客户端很可能在拥有对方请求的锁时,又同时请求对方已拥有的锁,这样便形成了死锁,在锁超时前,两个被阻塞的客户端都会占用一个服务端的处理线程,而服务器线程是非常稀缺的资源

HBase提供了几个特别的原子操作接口:

 checkAndPut/checkAndDelete/increment/append,这几个接口非常有用,内部实现也是基于行锁

checkAndPut/checkAndDelete内部调用代码片段:

      // Lock row
      Integer lid = getLock(lockId, get.getRow(), true);
      ......
      // get and compare
      try {
        result = get(get, false);
        ......
        //If matches put the new put or delete the new delete
        if (matches) {
          if (isPut) {
            internalPut(((Put) w), HConstants.DEFAULT_CLUSTER_ID, writeToWAL);
          } else {
            Delete d = (Delete)w;
            prepareDelete(d);
            internalDelete(d, HConstants.DEFAULT_CLUSTER_ID, writeToWAL);
          }
          return true;
        }
        return false;
      } finally {
        // release lock
        if(lockId == null) releaseRowLock(lid);
      }

实现逻辑:加锁=>get=>比较=>put/delete

checkAndPut在实际应用中非常有价值,我们线上生成Dpid的项目,多个客户端会并行生成DPID,如果有一个客户端已经生成了一个DPID,则其他客户端不能生成新的DPID,只能获取该DPID

代码片段:

			ret = hbaseUse.checkAndPut("bi.dpdim_mac_dpid_mapping", mac, "dim",
					"dpid", null, dpid);
			if(false == ret){
				String retDpid = hbaseUse.query("bi.dpdim_mac_dpid_mapping", mac, "dim", "dpid");
				if(!retDpid.equals(ABNORMAL)){
					return retDpid;
				}
			}else{
				columnList.add("mac");
				valueList.add(mac);
			}

checkAndPut详细试用可以参考:HBaseEveryDay_Atomic_compare_and_set

Reference:

HBase - Apache HBase (TM) ACID Properties

hbase源码解析之行锁

HBase权威指南

HBaseEveryDay_Atomic_compare_and_set

HBase原子性保证,布布扣,bubuko.com

时间: 2024-10-17 00:31:10

HBase原子性保证的相关文章

Redis集群方案及实现 - yfk的专栏 - 博客频道 - CSDN.NET

[公告]博客系统优化升级        Unity3D学习,离VR开发还有一步        博乐招募开始啦        虚拟现实,一探究竟 Redis集群方案及实现 2014-08-30 17:20     43035人阅读     评论(15)     收藏     举报 本文章已收录于: .embody{ padding:10px 10px 10px; margin:0 -20px; border-bottom:solid 1px #ededed; } .embody_b{ margin

hbase数据写入流程深度解析

2019/3/28 星期四hbase数据写入流程深度解析在看此链接之前,可以写查看 hbase读写请求详细解释 中的写请求流程 https://blog.51cto.com/12445535/2356085 简介:hbase设置之初就是为了应对大量的写多读少的应用,他出色的写性能,在一个100台RS的集群可以轻松地支撑每天10T的写入量.hbase的写数据流程大体分为3部分1.客户端的写入流程2.服务端的写入流程3.wal的工作原理 我们先回顾一下hbase写数据流程写请求处理过程小结1 cli

[ 译]Apache HBase Write Path

翻译自cloudera,原文直通车:Apache HBase Write Path Apache HBase也就是Hadoop Database是基于HDFS之上的.HBase可以随机获取和更新存储在HDFS上的记录.但是HDFS 上的文件只能追加而且一旦创建便无法修改.说到这里你或许会问:那HBase是怎么做到在HDFS上低延迟的读和写呢?在这篇 文章里,我们就会通过描述HBase的写的流程来解释数据在HBase中是如何更新的. 写流程描述就是HBase如何完成put或者delete操作.流程

深入理解Java虚拟机笔记---原子性、可见性、有序性

Java内存模型是围绕着并发过程中如何处理原子性.可见性.有序性这三个特征来建立的,下面是这三个特性的实现原理: 1.原子性(Atomicity) 由Java内存模型来直接保证的原子性变量操作包括read.load.use.assign.store和write六个,大致可以认为基础数据类型的访问和读写是具备原子性的.如果应用场景需要一个更大范围的原子性保证,Java内存模型还提供了lock和unlock操作来满足这种需求,尽管虚拟机未把lock与unlock操作直接开放给用户使用,但是却提供了更

Hbase写入hdfs源码分析

版权声明:本文由熊训德原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/258 来源:腾云阁 https://www.qcloud.com/community 本文档从源码角度分析了,hbase作为dfs client写入hdfs的hadoop sequence文件最终刷盘落地的过程.之前在<wal线程模型源码分析>中描述wal的写过程时说过会写入hadoop sequence文件,hbase为了保证数据的安全性,一般都

HBase二级索引与Join

转自:http://www.oschina.net/question/12_32573 二级索引与索引Join是Online业务系统要求存储引擎提供的基本特性.RDBMS支持得比较好,NOSQL阵营也在摸索着符合自身特点的最佳解决方案.这篇文章会以HBase做为对象来探讨如何基于Hbase构建二级索引与实现索引join.文末同时会列出目前已知的包括0.19.3版secondary index, ITHbase, Facebook和官方Coprocessor方案的介绍. 理论目标在HBase中实现

多线程程序中操作的原子性

[转]http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/ 多线程程序中操作的原子性 0. 背景 原子操作就 是不可再分的操作.在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制,同时也是一些常见的多线程Bug的源头.本文主要讨论了三 个问题:1. 多线程程序中对变量的读写操作是否是原子的?2. 多线程程序中对Bit field(位域)的读写操作是否是线程安全

hbase二级索引

二级索引与索引Join是多数业务系统要求存储引擎提供的基本特性,RDBMS早已支持,NOSQL阵营也在摸索着符合自身特点的最佳解决方案.这篇文章会以HBase做为对象来讨论如何基于Hbase构建二级索引与实现索引join.文末同时会列出目前已知的包括0.19.3版secondary index, ITHbase, Facebook方案和官方Coprocessor的介绍. 理论目标在HBase中实现二级索引与索引Join需要考虑三个目标:1,高性能的范围检索.2,数据的低冗余(存储所占的数据量).

HBase安装及简单使用

通过之前的hadoop0.20.2的安装并调试成功,接下来我们继续安装hbase0.90.5.在安装hbase0.90.5之前,因为hbase0.90.5只支持jdk1.6,所以,我把之前的jdk1.8卸载,重新安装了jdk1.6.  第一步: 首先需要下载hbase0.90.5.tar.gz,并解压到/home/hadoop/的目录下,同时将目录修改为hbase0.90.5 第二步: 替换hadoop核心jar包,主要母的是防止hbase和hadoop版本不同出现兼容问题,造成hmaster启