对应 HBase 版本0.94.1,对照了开源的版本和工做使用的某发行版shell
问题:在 HBase shell 里面输入 flush 'table_or_region_name'
以后,发生了什么?具体的实现是怎么样的?对于现有的某个表,我如何在作操做以前估算 flush 执行的时间?apache
HBase shell 使用 ruby 实现,在 putty 敲hbase shell
,调用的是${HBASE_HOME}/bin/hbase
这个 bash 脚本,根据shell
这个参数,触发调用 ruby 代码,相关的部分以下:ruby
if [ "$COMMAND" = "shell" ] ; then if [ "$JRUBY_HOME" != "" ] ; then CLASSPATH="$JRUBY_HOME/lib/jruby.jar:$CLASSPATH" HBASE_OPTS="$HBASE_OPTS -Djruby.home=$JRUBY_HOME -Djruby.lib=$JRUBY_HOME/lib" fi CLASS="org.jruby.Main -X+O ${JRUBY_OPTS} ${HBASE_HOME}/bin/hirb.rb"
在 hirb.rb 里面,引入相关的包(${HBASE_HOME}/lib/ruby
目录下),而后启动一个运行的 CLI 环境。bash
进入正题了。数据结构
在 hbase shell 里面,全部执行的命令,都在${HBASE_HOME}/lib/ruby/shell/commands
目录下,有对应的${COMMAND}.rb
的对应文件。并发
找到 flush.rb,核心代码以下:oop
def command(table_or_region_name) format_simple_command do admin.flush(table_or_region_name) end end
这里调用了 admin.rb 这个文件里面的方法:this
@admin = org.apache.hadoop.hbase.client.HBaseAdmin.new(configuration) def flush(table_or_region_name) @admin.flush(table_or_region_name) end
到这里,就找到了 Java 程序的入口,调用了 HBaseAdmin.flush(table_or_region_name)
这个方法。
后续几部分的类图以下:spa
HBaseAdmin 类下面包含了三个 flush 方法:code
public void flush(String tableNameOrRegionName) throws IOException, InterruptedException {} public void flush(byte[] tableNameOrRegionName) throws IOException, InterruptedException {} private void flush(ServerName sn, HRegionInfo hri) throws IOException {}
第一个略过。
第二个,逻辑清晰:
注意
第三个,对每一个 Region 进行 flush,其实是第二个 flush 中全部 case 最终的归宿。
在第三个 flush 中,实现代码以下:
HRegionInterface rs = this.connection.getHRegionConnection(sn.getHostname(), sn.getPort()); rs.flushRegion(hri);
HRegionInterface 是一个抽象接口,flushRegion 是一个抽象方法。在0.94.1这个版本下,只有 HRegionServer 实现了 HRegionInterface 接口,因此要在 HRegionServer 里面找到具体的代码实现。
在 HRegionServer 类里面,包含了三个 flush 的实现:
public void flushRegion(byte[] regionName) throws IllegalArgumentException, IOException {} public void flushRegion(byte[] regionName, long ifOlderThanTS) throws IllegalArgumentException, IOException {} @QosPriority (priority=100) public void flushRegion(HRegionInfo regionInfo) throws NotServingRegionException, IOException {}
region.flushcache()
checkOpen()
检查 RegionServer 在线后,调用region.flushcache()
接下来,查看看下 HRegion 类下面flushcache()
的实现。
flushcache只是个入口方法,会作一些 flush 以前的准备工做,包括:创建任务状态监控、判断 Coprocessor、处理未 WAL 的 put 、写加锁等。以后,调用内部方法internalFlushcache
开始flush。
在 internalFlushcache 方法实现中,作了 MVCC 的一些工做,最终,调用了StoreFlusher的flushCache方法实现。
internalFlushcache 为了保证数据一致性作了不少的检查、校验、加锁,目前功力不够,先标记下,进入下一层。
看下 StoreFlusher 的实现。
StoreFlusher 是个接口,在0.94.1这个版本里面,只有 Store.StoreFlusherImpl 一个实现类。
在 StoreFlusher 接口里面能够看到,flush 操做执行的过程当中包含3个部分:
StoreFlusherImpl 是 Store 类的内部私有类,前面提到的 StoreFlusher 的3个方法,由 StoreFlusherImpl实现后,prepare 是本身实现,flushCache和 commit 都是调用外部 Store类的方法来完成。
public void prepare() { memstore.snapshot(); this.snapshot = memstore.getSnapshot(); this.snapshotTimeRangeTracker = memstore.getSnapshotTimeRangeTracker(); }
调用了 MemStore 的方法,作快照。
从 StoreFlusherImpl 调用 Store 类的flushCache方法,包装了internalFlushCache
方法来实现。
逻辑比较清晰:
StoreFlusherImpl 类的 commit 方法首先调用外部 Store类的commitFile
方法,主要作的事情有两件:
而后会调用外部 Store类的updateStorefiles
更新 Store 类的 storefile,更新文件后,须要调用needsCompaction()
,查看下是否由于本次 flush 执行形成的文件变化会触发 Compaction。若是触发 Compaction,会启动 Compaction 相关的一套机制继续执行,后续再单独介绍。
至此,手动 flush 操做背后的实现,初步梳理完毕。前面只是一个调用路径的梳理,后面继续丰富和补充。