[HBase]Flush

转载自:http://iwinit.iteye.com/blog/1827404

Flush过程,对应MemStoreFlusher

1.是否需要做global flush,有则取当前rs最大的region进行flush

Java代码   收藏代码
  1. if (isAboveLowWaterMark()) {  
  2.            ....  
  3.     //获取memstore最大的region进行flush  
  4.            if (!flushOneForGlobalPressure()) {  
  5.              // Wasn't able to flush any region, but we're above low water mark  
  6.              // This is unlikely to happen, but might happen when closing the  
  7.              // entire server - another thread is flushing regions. We'll just  
  8.              // sleep a little bit to avoid spinning, and then pretend that  
  9.              // we flushed one, so anyone blocked will check again  
  10.              lock.lock();  
  11.              try {  
  12.                Thread.sleep(1000);  
  13.                flushOccurred.signalAll();  
  14.              } finally {  
  15.                lock.unlock();  
  16.              }  
  17.            }  
  18.     //完了,再requeue一个任务,再次check是否超过内存限制  
  19.            // Enqueue another one of these tokens so we'll wake up again  
  20.            wakeupFlushThread();  

2.region flush开始

3.检查region下的store file是否超过限制,默认7个,超过则重试(requeue)

Java代码   收藏代码
  1. if (!fqe.region.getRegionInfo().isMetaRegion() &&  
  2.         isTooManyStoreFiles(region)) {  
  3.       ......  
  4.   
  5.         // Put back on the queue.  Have it come back out of the queue  
  6.         // after a delay of this.blockingWaitTime / 100 ms.  
  7.         this.flushQueue.add(fqe.requeue(this.blockingWaitTime / 100));  
  8.         // Tell a lie, it's not flushed but it's ok  
  9.         return true;  
  10.       }  
  11.     }  

 4.拿Region的读锁,阻塞写

5.MVCC里增加一个事务,代表flush操作

Java代码   收藏代码
  1. w = mvcc.beginMemstoreInsert();  
  2.      mvcc.advanceMemstore(w);  

 6.拿Log sequence id

7.take snapshot,kvList引用切换

Java代码   收藏代码
  1. void snapshot() {  
  2.    this.lock.writeLock().lock();  
  3.    try {  
  4.      // If snapshot currently has entries, then flusher failed or didn't call  
  5.      // cleanup.  Log a warning.  
  6.      if (!this.snapshot.isEmpty()) {  
  7.        LOG.warn("Snapshot called again without clearing previous. " +  
  8.          "Doing nothing. Another ongoing flush or did we fail last attempt?");  
  9.      } else {  
  10.        if (!this.kvset.isEmpty()) {  
  11. //引用切换  
  12.          this.snapshot = this.kvset;  
  13.          this.kvset = new KeyValueSkipListSet(this.comparator);  
  14.          this.snapshotTimeRangeTracker = this.timeRangeTracker;  
  15.          this.timeRangeTracker = new TimeRangeTracker();  
  16.          // Reset heap to not include any keys  
  17.          this.size.set(DEEP_OVERHEAD);  
  18.          // Reset allocator so we get a fresh buffer for the new memstore  
  19.          if (allocator != null) {  
  20.            this.allocator = new MemStoreLAB(conf);  
  21.          }  
  22.        }  
  23.      }  
  24.    } finally {  
  25.      this.lock.writeLock().unlock();  
  26.    }  
  27.  }  

 8.MVCC等待之前的事务完成

Java代码   收藏代码
  1. mvcc.waitForRead(w);  

 9.将内存中的kv数据flush到hfile,流程如下

10.生成一个临时目录,使用UUID生成一个文件名

11.使用StoreScanner遍历内存中的kv数据,循环append入write cache

12.writer最终flush到hfile

Java代码   收藏代码
  1. private Path internalFlushCache(final SortedSet<KeyValue> set,  
  2.       final long logCacheFlushId,  
  3.       TimeRangeTracker snapshotTimeRangeTracker,  
  4.       AtomicLong flushedSize,  
  5.       MonitoredTask status)  
  6.       throws IOException {  
  7.     StoreFile.Writer writer;  
  8.     // Find the smallest read point across all the Scanners.  
  9.     long smallestReadPoint = region.getSmallestReadPoint();  
  10.     long flushed = 0;  
  11.     Path pathName;  
  12.     // Don't flush if there are no entries.  
  13.     if (set.size() == 0) {  
  14.       return null;  
  15.     }  
  16.     //scan方式扫描KVlist数据,注意内存中的kv数据是有序的,先rowkey排序,再按family和qualifier,再按Timestamp  
  17.     Scan scan = new Scan();  
  18.     scan.setMaxVersions(scanInfo.getMaxVersions());  
  19.     // Use a store scanner to find which rows to flush.  
  20.     // Note that we need to retain deletes, hence  
  21.     // treat this as a minor compaction.  
  22.     InternalScanner scanner = new StoreScanner(this, scan, Collections  
  23.         .singletonList(new CollectionBackedScanner(set, this.comparator)),  
  24.         ScanType.MINOR_COMPACT, this.region.getSmallestReadPoint(),  
  25.         HConstants.OLDEST_TIMESTAMP);  
  26.     try {  
  27.       // TODO:  We can fail in the below block before we complete adding this  
  28.       // flush to list of store files.  Add cleanup of anything put on filesystem  
  29.       // if we fail.  
  30.       synchronized (flushLock) {  
  31.         status.setStatus("Flushing " + this + ": creating writer");  
  32.         // A. Write the map out to the disk  
  33.         writer = createWriterInTmp(set.size());  
  34.         writer.setTimeRangeTracker(snapshotTimeRangeTracker);  
  35.     //临时目录  
  36.         pathName = writer.getPath();  
  37.         try {  
  38.           List<KeyValue> kvs = new ArrayList<KeyValue>();  
  39.           boolean hasMore;  
  40.           do {  
  41.             hasMore = scanner.next(kvs);  
  42.             if (!kvs.isEmpty()) {  
  43.               for (KeyValue kv : kvs) {  
  44.                 // If we know that this KV is going to be included always, then let us  
  45.                 // set its memstoreTS to 0. This will help us save space when writing to disk.  
  46.                 if (kv.getMemstoreTS() <= smallestReadPoint) {  
  47.                   // let us not change the original KV. It could be in the memstore  
  48.                   // changing its memstoreTS could affect other threads/scanners.  
  49.                   kv = kv.shallowCopy();  
  50.                   kv.setMemstoreTS(0);  
  51.                 }  
  52.         //往cache中写数据  
  53.                 writer.append(kv);  
  54.                 flushed += this.memstore.heapSizeChange(kv, true);  
  55.               }  
  56.               kvs.clear();  
  57.             }  
  58.           } while (hasMore);  
  59.         } finally {  
  60.           // Write out the log sequence number that corresponds to this output  
  61.           // hfile.  The hfile is current up to and including logCacheFlushId.  
  62.           status.setStatus("Flushing " + this + ": appending metadata");  
  63.           writer.appendMetadata(logCacheFlushId, false);  
  64.           status.setStatus("Flushing " + this + ": closing flushed file");  
  65.     //flush到HDFS  
  66.           writer.close();  
  67.         }  
  68.       }  
  69.     }  
  70. ......  
  71.   }  

13.将store file从tmp下move到正式目录,并添加到Store file列表

14.清理snapshot

Java代码   收藏代码
  1. private boolean updateStorefiles(final StoreFile sf,  
  2.                                    final SortedSet<KeyValue> set)  
  3.   throws IOException {  
  4.     this.lock.writeLock().lock();  
  5.     try {  
  6.     //添加到storeFile中  
  7.       ArrayList<StoreFile> newList = new ArrayList<StoreFile>(storefiles);  
  8.       newList.add(sf);  
  9.       storefiles = sortAndClone(newList);  
  10.     //释放内存  
  11.       this.memstore.clearSnapshot(set);  
  12.     } finally {  
  13.       // We need the lock, as long as we are updating the storefiles  
  14.       // or changing the memstore. Let us release it before calling  
  15.       // notifyChangeReadersObservers. See HBASE-4485 for a possible  
  16.       // deadlock scenario that could have happened if continue to hold  
  17.       // the lock.  
  18.       this.lock.writeLock().unlock();  
  19.     }  
  20.   
  21.     // Tell listeners of the change in readers.  
  22.     notifyChangedReadersObservers();  
  23.   
  24.     return needsCompaction();  
  25.   }  

 15.修改global和memstore的内存大小

Java代码   收藏代码
  1. public long addAndGetGlobalMemstoreSize(long memStoreSize) {  
  2.   if (this.rsAccounting != null) {  
  3.     rsAccounting.addAndGetGlobalMemstoreSize(memStoreSize);  
  4.   }    
  5.   return this.memstoreSize.getAndAdd(memStoreSize);  
  6. }  

16.flush成功后,HLog增加一条flush信息,小于该flush txid的事务已经失效了

Java代码   收藏代码
  1.  public void completeCacheFlush(final byte [] encodedRegionName,  
  2.      final byte [] tableName, final long logSeqId, final boolean isMetaRegion)  
  3.  throws IOException {  
  4.    long start = System.currentTimeMillis();  
  5.    try {  
  6.      if (this.closed) {  
  7.        return;  
  8.      }  
  9.      long txid = 0;  
  10.      synchronized (updateLock) {  
  11. //flush的事务数据  
  12.        WALEdit edit = completeCacheFlushLogEdit();  
  13.        HLogKey key = makeKey(encodedRegionName, tableName, logSeqId,  
  14.            System.currentTimeMillis(), HConstants.DEFAULT_CLUSTER_ID);  
  15.        logSyncerThread.append(new Entry(key, edit));  
  16.        txid = this.unflushedEntries.incrementAndGet();  
  17.        this.numEntries.incrementAndGet();  
  18.      }  
  19.      // sync txn to file system  
  20. //写入HDFS  
  21.      this.sync(txid);  
  22.   
  23.    } finally {  
  24.      // updateLock not needed for removing snapshot's entry  
  25.      // Cleaning up of lastSeqWritten is in the finally clause because we  
  26.      // don't want to confuse getOldestOutstandingSeqNum()  
  27.      this.lastSeqWritten.remove(getSnapshotName(encodedRegionName));  
  28.      this.cacheFlushLock.unlock();  
  29.    }  
  30.    long took = System.currentTimeMillis() - start;  
  31.    doWALTime.inc(took);  
  32.  }  

 17.唤醒等待的业务线程


你可能感兴趣的:([HBase]Flush)