您的位置:首页 > 其它

HBase 0.1.0 Flush流程源码分析

2012-12-04 22:11 417 查看
这篇文章将会分析和总结Flush流程的实现。

Flush是LSM-Tree重要的实现步骤,对应理解hbase非常关键。

简单来说,flush就是把内存中的数据flush到磁盘上,那么具体是怎么实现的纳?

首先,regionserver在适当的时机调用region.flushcache。步骤如下:

lock.readLock().lock();                      // Prevent splits and closes
try {
long startTime = -1;
synchronized (updateLock) {// Stop updates while we snapshot the memcaches
startTime = snapshotMemcaches();
}
return internalFlushcache(startTime);
} finally {
lock.readLock().unlock();
}


可以看到主要有2个步骤:

1.snapshot memcache。

// HRegion的 snapshot
this.memcacheSize.set(0L);
// 每个HStore都要snapshot
for (HStore hstore: stores.values()) {
hstore.snapshotMemcache();
}
1.把本region的memcachesize设为0,以便后续的操作可以正确的赋值
2.每个HStore snapshot

//每个HStore的snapshot算法:把memcache的数据放到snapshot中,clear memcache
this.lock.writeLock().lock();
try {
synchronized (memcache) {
if (memcache.size() != 0) {
snapshot.putAll(memcache);
memcache.clear();
}
}
} finally {
this.lock.writeLock().unlock();
}


2.internalFlushcache

Region层次的internalFlushcache比较清晰,主要就是三个步骤:

1.拿到flush的操作id

2。每个HStore flushcache

3.记录flush完成到hlog中,后续的操作就不会被flush影响到了

long sequenceId = log.startCacheFlush();

for (HStore hstore: stores.values()) {
hstore.flushCache(sequenceId);
}
this.log.completeCacheFlush(this.regionInfo.getRegionName(),
regionInfo.getTableDesc().getName(), sequenceId);


再来看下每个HStore是怎么flush的:

1.创建一个新的HStorefile文件。

// A. Write the Maps out to the disk
HStoreFile flushedFile = new HStoreFile(conf, fs, basedir,
info.getEncodedName(), family.getFamilyName(), -1L, null);
String name = flushedFile.toString();
MapFile.Writer out = flushedFile.getWriter(this.fs, this.compression,
this.bloomFilter);


2.拿出HRegion memcache里的记录一条条的append到新建的 HStoreFile里,中间需要filter一下cf,即把内存中的记录分别flush到对应cf的store中
for (Map.Entry<HStoreKey, byte []> es: cache.entrySet()) {
HStoreKey curkey = es.getKey();
TextSequence f = HStoreKey.extractFamily(curkey.getColumn());
if (f.equals(this.family.getFamilyName())) {
entries++;
out.append(curkey, new ImmutableBytesWritable(es.getValue()));
}
}
3.把log num写入storefile,把bloomfilter写入磁盘
// B. Write out the log sequence number that corresponds to this output
// MapFile.  The MapFile is current up to and including the log seq num.
flushedFile.writeInfo(fs, logCacheFlushId);

// C. Flush the bloom filter if any
if (bloomFilter != null) {
flushBloomFilter();
}
4..把当前的storefile的reader加入到该HStore的readers里,把storefile加到storefiles里
Long flushid = Long.valueOf(logCacheFlushId);
// Open the map file reader.
this.readers.put(flushid,
flushedFile.getReader(this.fs, this.bloomFilter));
this.storefiles.put(flushid, flushedFile);


这边要详细说明下flushedFile.getWriter方法。
HBase封装了3层HStorefile的操作类处理对HStorefile的操作。类的层次关系如下:

HalfMapFile(for split)

| |

| |

HalfMapFileReader

^

|

|

Hbase.BloomFilterMapFile (read: 如果bloomfilter不为空则用bloomfilter计算是否存在,如果存在则get)

| | (writer:如果bloomfilter存在,则每个key都写bloomfilter)

| |

Reader Writer

^

|

|

Hadoop.MapFile (基类,基本不使用)

| |

| |

HbaseReader HbaseWriter

OK,那么让我们看下HStorefile.getWriter是怎么写的

如果HStorefile是reference,代表是split的中间文件,split文件不需要write,报错

如果HStorefile不是reference,则返回BloomFilterMapFile.Writer。

if (isReference()) {
throw new IOException("Illegal Access: Cannot get a writer on a" +
"HStoreFile reference");
}
return new BloomFilterMapFile.Writer(conf, fs,
getMapFilePath().toString(), HStoreKey.class,
ImmutableBytesWritable.class, compression, bloomFilter);


因此,在flush中实际调用了BloomFilterMapFile.Writer,先写入bloomfilter,再写入文件。

总结一下:

1.flush不是每次只生成一个storefile,而是每个HStore生成一个storefile。

2.flush是批量append,因此效率很高,而且如果是有bloomfilter就会写bloomfilter

3.这边snapshot的概念好像跟后面的版本概念不一样,因此就不研究了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: