hbase 源代码分析 (13) memStore flush to HFile

在第七节put过程中首先写入memStore,在 操作的最后会调用一次判断是否需要写入到HFile中。

memStore flush to Hfile 流程如下:
1)判断是否需要写入到HFile中,判断大小值1024×1024×128  =128M,可以通过修改hbase.hregion.memstore.flush.size配置
2)在flush 过程首先将mem 数据new FlushRegionEntry 然后方法队列中
3)MemStoreFlusher里启动的run会不断去判断是否需要写数据。
4)写完之前会判断是否需要spit和comp ,同样flush结束之后也做判断。
5)写的过程中,需要先写快照,然后写成tmp文件,然后再移动文件变成正式文件。
6)里面涉及很多WAL和MVCC,已经region,row的锁操作,
7)写tmp主要是定义一个Write 然后 调用append,然后写到HFile。


1)flush 入口 put过程,调用HRegin类:
   
   
  1. @Override
  2. public void processRowsWithLocks(RowProcessor<?,?> processor, long timeout,
  3. long nonceGroup, long nonce) throws IOException {
  4. //写入内存。平统计占用多大内存
  5. {
  6. // 8. Apply to memstore
  7. Store store = getStore(cell);
  8. addedSize += store.add(cell);
  9. } finally {
  10. closeRegionOperation();
  11. if (!mutations.isEmpty() &&
  12. //如果本region大于128M就开始Flush调用Flush过程
  13. isFlushSize(this.addAndGetGlobalMemstoreSize(addedSize))) {
  14. requestFlush();
  15. }
  16. }
  17. }
他会调用MemStoreFlusher.java里的requestFlush方法,这方法会new一个FlushRegionEntry然后放入到队列中去。而这 MemStoreFlusher是regionService start时候启动的。这个可以看我的上一个章节HMaster 和RegionService 启动过程。
这个MemStoreFlusher 里面又new 一个FlushHandler,这个handler就不停处理上面方法放入队列的 FlushRegionEntry
放入队列方法类 MemStoreFlusher中
   
   
  1. @Override
  2. public void requestFlush(Region r, boolean forceFlushAllStores) {
  3. synchronized (regionsInQueue) {
  4. if (!regionsInQueue.containsKey(r)) {
  5. // This entry has no delay so it will be added at the top of the flush
  6. // queue. It'll come out near immediately.
  7. FlushRegionEntry fqe = new FlushRegionEntry(r, forceFlushAllStores);
  8. this.regionsInQueue.put(r, fqe);
  9. this.flushQueue.add(fqe);
  10. }
  11. }
  12. }
读队列 MemStoreFlusher :里面删除很多东西。留下主要要的。
   
   
  1. private class FlushHandler extends HasThread {
  2. @Override
  3. public void run() {
  4.         //死循环
  5. while (!server.isStopped()) {
  6. FlushQueueEntry fqe = null;
  7. try {
  8. fqe = flushQueue.poll(threadWakeFrequency, TimeUnit.MILLISECONDS);
  9. 。。。//这里删除了很多。
  10.         //调用刷新
  11. if (!flushRegion(fre)) {
  12. break;
  13. }
  14. }
  15. }

这个里面会判断是否需要spit,spit 过程留到下一个章节去分析。
   
   
  1. private boolean flushRegion(final FlushRegionEntry fqe) {
  2. Region region = fqe.region;
  3. //首先会去判断是否太多文件,如果文件太大,可能需要spit,或者compation
  4. if (!region.getRegionInfo().isMetaRegion() &&
  5. isTooManyStoreFiles(region)) {
  6. if (!this.server.compactSplitThread.requestSplit(region)) {
  7. try {
  8. this.server.compactSplitThread.requestSystemCompaction(
  9. region, Thread.currentThread().getName());
  10. }
  11. }
  12. }
  13. }
  14. //做完上面的时候会flush
  15. return flushRegion(region, false, fqe.isForceFlushAllStores());
  16. }
这个memStoreFlusher的flushRegion方法首先会去刷新,然后判断是否需要spit
   
   
  1. private boolean flushRegion(final Region region, final boolean emergencyFlush,
  2. boolean forceFlushAllStores) {
  3. //判断是否已经在处理了。已经省略
  4. lock.readLock().lock();
  5. try {
  6. notifyFlushRequest(region, emergencyFlush);
  7.     //进行flush
  8. FlushResult flushResult = region.flush(forceFlushAllStores);
  9. boolean shouldCompact = flushResult.isCompactionNeeded();
  10. //写到Hfile之后判断是否需要spit 或者需要shouldCompact
  11. boolean shouldSplit = ((HRegion)region).checkSplit() != null;
  12. if (shouldSplit) {
  13. this.server.compactSplitThread.requestSplit(region);
  14. } else if (shouldCompact) {
  15. server.compactSplitThread.requestSystemCompaction(
  16. region, Thread.currentThread().getName());
  17. }
  18. }
之后会调用HRegion的 flushcache方法。
   
   
  1. public FlushResult flushcache(boolean forceFlushAllStores, boolean writeFlushRequestWalMarker)
  2. throws IOException {
  3. lock.readLock().lock();
  4. try {
  5. try {
  6. Collection<Store> specificStoresToFlush =
  7. forceFlushAllStores ? stores.values() : flushPolicy.selectStoresToFlush();
  8. FlushResult fs = internalFlushcache(specificStoresToFlush,
  9. status, writeFlushRequestWalMarker);
  10. }
然后 internalFlushcache 会调用
   
   
  1. protected FlushResult internalFlushcache(final WAL wal, final long myseqid,
  2. final Collection<Store> storesToFlush, MonitoredTask status, boolean writeFlushWalMarker)
  3. throws IOException {
  4.     //准备阶段,主要写快照。
  5. PrepareFlushResult result
  6. = internalPrepareFlushCache(wal, myseqid, storesToFlush, status, writeFlushWalMarker);
  7. if (result.result == null) {
  8.     //将文件写入到正式的文件目录下
  9. return internalFlushCacheAndCommit(wal, status, result, storesToFlush);
  10. } else {
  11. return result.result; // early exit due to failure from prepare stage
  12. }
  13. }

//写快照方法
   
   
  1. protected PrepareFlushResult internalPrepareFlushCache(final WAL wal, final long myseqid,
  2. final Collection<Store> storesToFlush, MonitoredTask status, boolean writeFlushWalMarker)
  3. throws IOException {
  4. this.updatesLock.writeLock().lock();
  5. Set<byte[]> flushedFamilyNames = new HashSet<byte[]>();
  6. for (Store store: storesToFlush) {
  7. flushedFamilyNames.add(store.getFamily().getName());
  8. }
  9. //多版本
  10. MultiVersionConcurrencyControl.WriteEntry writeEntry = mvcc.begin();
  11. //多版本结束
  12. mvcc.completeAndWait(writeEntry);
  13. writeEntry = null;
  14. try {
  15. try {
  16. for (Store s : storesToFlush) {
  17. totalFlushableSizeOfFlushableStores += s.getFlushableSize();
  18. }
  19. // 快照 WAL
  20. if (wal != null && !writestate.readOnly) {
  21. FlushDescriptor desc = ProtobufUtil.toFlushDescriptor(FlushAction.START_FLUSH,
  22. getRegionInfo(), flushOpSeqId, committedFiles);
  23. // no sync. Sync is below where we do not hold the updates lock
  24. trxId = WALUtil.writeFlushMarker(wal, this.htableDescriptor, getRegionInfo(),
  25. desc, false, mvcc);
  26. }
  27. //写快照
  28. for (StoreFlushContext flush : storeFlushCtxs.values()) {
  29. flush.prepare();
  30. }
  31. finally {
  32. this.updatesLock.writeLock().unlock();
  33. }
    1. // 等待WAL 写到HLog中   
  34. wal.sync(); // ensure that flush marker is sync'ed
  35. mvcc.complete(writeEntry);
  36. }

//flush 到HFile,删除很多。留下几个关键位置,首先写入tmp目录,下面,然后rename一下就行。最后还会将空间减少。
   
   
  1. protected FlushResult internalFlushCacheAndCommit(
  2. final WAL wal, MonitoredTask status, final PrepareFlushResult prepareResult,
  3. final Collection<Store> storesToFlush)
  4. throws IOException {
  5. //写到temp文件
  6. for (StoreFlushContext flush : storeFlushCtxs.values()) {
  7. flush.flushCache(status);
  8. }
  9. // Switch snapshot (in memstore) -> new hfile (thus causing
  10. // all the store scanners to reset/reseek).
  11. Iterator<Store> it = storesToFlush.iterator();
  12. // stores.values() and storeFlushCtxs have same order
  13. for (StoreFlushContext flush : storeFlushCtxs.values()) {
  14. boolean needsCompaction = flush.commit(status);
  15. if (needsCompaction) {
  16. compactionRequested = true;
  17. }
  18. this.addAndGetGlobalMemstoreSize(-totalFlushableSizeOfFlushableStores);
  19. }
flushCache 主要定义一个Write ,将snapShot 写入tmp文件。
flush . commit ( status ); 这是 flushCache的返回结果目录,移动到正式目录下面。

到此文件都写入文件结束。
如果写入文件还有什么疑问,可以留言。



http://blog.csdn.net/chenfenggang/article/details/75195041









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值