HBase row lock principle and implementation

  hbase mutation operations, such as delete put, etc., need to obtain row locks, and then operate in acquiring row locks, is carried out by HRegion.getRowLockInternal (byte [] row, boolean waitForLock), therefore, we first general browsing about the process of this method, as follows. Can be seen, the method relates to the line lock and associated content RowLock RowLockContext two classes. Both are HRegion inner class, the following detailed look at how these two classes is implemented.

protected RowLock getRowLockInternal(byte[] row, boolean waitForLock) throws IOException {
    HashedBytes rowKey = new HashedBytes(row);
    RowLockContext rowLockContext = new RowLockContext(rowKey);

    // loop until we acquire the row lock (unless !waitForLock)
    while (true) {
      RowLockContext existingContext = lockedRows.putIfAbsent(rowKey, rowLockContext);
      if (existingContext == null) {
        // Row is not already locked by any thread, use newly created context.
        break;
      } else if (existingContext.ownedByCurrentThread()) {
        // Row is already locked by current thread, reuse existing context instead.
        rowLockContext = existingContext;
        break;
      } else {
        if (!waitForLock) {
          return null;
        }
        try {
          // Row is already locked by some other thread, give up or wait for it
          if (!existingContext.latch.await(this.rowLockWaitDuration, TimeUnit.MILLISECONDS)) {
            throw new IOException("Timed out waiting for lock for row: " + rowKey);
          }
        } catch (InterruptedException ie) {
          LOG.warn("Thread interrupted waiting for lock on row: " + rowKey);
          InterruptedIOException iie = new InterruptedIOException();
          iie.initCause(ie);
          throw iie;
        }
      }
    }

    // allocate new lock for this thread
    return rowLockContext.newLock();
  }

  See RowLock first class, which is a main logic release method, the line is used to release the lock. At the same time there is a Boolean parameter release, the default is false, the representative of whether the row lock is released out.

public static class RowLock {
    @VisibleForTesting final RowLockContext context;
    private boolean released = false;

    @VisibleForTesting RowLock(RowLockContext context) {
      this.context = context;
    }

    /**
     * Release the given lock.  If there are no remaining locks held by the current thread
     * then unlock the row and allow other threads to acquire the lock.
     * @throws IllegalArgumentException if called by a different thread than the lock owning thread
     */
    public void release() {
      if (!released) {
        context.releaseLock();
        released = true;
      }
    }
  }

  But in RowLock, and did not see the actual information related to the lock, which is Zehui Shi, do not worry, look carefully release method, which has a context, is RowLockContext type. While the method is also configured to pass a context object, it was suspected in the RowLockContext out a new ROWLOCK, look into the RowLockContext:

{Class RowLockContext @VisibleForTesting 
    Private HashedBytes Final Row; 
  // lock achieved condition by counting the rows and CountDownLatch. The reason here will countdownlatch set to one, because hbase do not know in the end how much condition to compete lock, so add a count lockCount,
  // when lockCount is zero, then latch.coutDown. Otherwise it will await the getRowLockInternal in.
Final LATCH = a CountDownLatch new new Private a CountDownLatch (. 1); Private Final the Thread Thread; Private LockCount int = 0; RowLockContext (HashedBytes Row) { this.row = Row; this.thread Thread.currentThread = (); } Boolean ownedByCurrentThread () { Thread == Thread.currentThread return (); } ROWLOCK newLock () { LockCount ++; return new new ROWLOCK (the this); } void releaseLock() { if (!ownedByCurrentThread()) { throw new IllegalArgumentException("Lock held by thread: " + thread + " cannot be released by different thread: " + Thread.currentThread()); } lockCount--; if (lockCount == 0) { // no remaining locks by the thread, unlock and allow other threads to access RowLockContext existingContext = lockedRows.remove(row); if (existingContext != this) { throw new RuntimeException( "Internal row lock state inconsistent, should not happen, row: " + row); } latch.countDown(); } } }

  Lock condition achieved by counting the rows and CountDownLatch. The reason here will countdownlatch set to one, because hbase do not know in the end how much condition to compete lock, so add a count lockCount,
When lockCount is zero, then latch.coutDown. Otherwise it will await the getRowLockInternal in. 
  In HRegion There is also a key member variable: lockedrows, used to store the current row has acquired all the information line lock, key to rowkey, value is RowLockContext.
// map from a locked row to the context for that lock including:
  // - CountDownLatch for threads waiting on that row
  // - the thread that owns the lock (allow reentrancy)
  // - reference count of (reentrant) locks held by the thread
  // - the row itself
  private final ConcurrentHashMap<HashedBytes, RowLockContext> lockedRows =
      new ConcurrentHashMap<HashedBytes, RowLockContext>();
  Well, the contents of row locks involved, we have a general view, and then began to pass again from getRowLockInternal in logic:
  1. Construction of objects according to rowkey RowLockContext
  2. while loop until the line is acquired lock, or wait timeout
    1. First determines whether there is a row lock information rowkey lockedrows, the use here of putIfAbsent concurrentMap
      1. If not, this line of thought the lock, no other thread to get the information into lockedrows row locks, the direct break out of the loop, and now a row lock.
      2. If so, the row that the lock has been occupied, the logic is as follows
        1. Determine whether the bank holds a lock thread is itself, if it is, will overwrite rowLockContext, out of the loop
        2. Whether it is necessary wait a row lock, parameter waitForLock, if you do not wait a direct return; If you wait, then call latch.await wait, if the timeout exception is thrown.
    2. If you jumped out of the loop, it means that the acquisition is successful, the newLock and return.

   The above process is to obtain row locks, release row locks it, is achieved by releaseRowLocks HRegion way, we look at the code:

 /**
   * If the given list of row locks is not null, releases all locks.
   */
  public void releaseRowLocks(List<RowLock> rowLocks) {
    if (rowLocks != null) {
      for (RowLock rowLock : rowLocks) {
        rowLock.release();
      }
      rowLocks.clear();
    }
  }

  Visible call RowLock.release achieved, this method has the above code, specific logic is as follows:

      The deleted row lock in lockedrows in.

      Release is determined whether or false, if it is false, the call context.releaseLock, context.releaseLock logic is as follows

        First, determine whether to release the row lock thread is the holder of that row lock, if not an exception is thrown

        The count--;

        If the count == 0, and then directly call latch.countDown, this method will trigger other threads to acquire row locks. When the count == 0 means that the thread has a lock does not need to be diverted, has been released

        The release is set to true.

Note :

Here in getRowLockInternal, as long as lockedRows.putIfAbsent (rowKey, rowLockContext) is successful, other threads will not succeed by concurrentMap guarantee.

Guess you like

Origin www.cnblogs.com/Evil-Rebe/p/11323010.html