HBase 从0.95开始引入了Snapshot,可以对table进行Snapshot,也可以Restore到Snapshot。Snapshot可以在线做,也可以离线做。Snapshot的实现不涉及到table实际数据的拷贝,仅仅拷贝一些元数据,比如组成table的region info,表的descriptor,还有表对应的HFile的文件的引用。本文基于0.98.4

Snapshot命令如下所示:

hbase> snapshot 'sync_stage:Photo', 'PhotoSnapshot' //对sync_stage这个namespace下的Photo表做一次snapshot(表只有一个column family,叫做PHOTO),snapshot名字叫做PhotoSnapshot

这个Snapshot执行后,所有相关的元数据都会被保存在(假设hbase.rootdir设置为/sync/hbase) hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot目录中。如下所示:

$ bin/hadoop fs -ls -R hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/.snapshotinfo //Snapshot的一些描述信息
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/.tabledesc
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/.tabledesc/.tableinfo.0000000001 //Photo表的HTableDescriptor的序列化
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/.tmp
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/0595cf25f61de0f1c3ddf38e50a59b07 //Photo表有三个region,这里显示region encode name
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/0595cf25f61de0f1c3ddf38e50a59b07/.regioninfo // region的HRegionInfo序列化
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/0595cf25f61de0f1c3ddf38e50a59b07/PHOTO
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/0595cf25f61de0f1c3ddf38e50a59b07/PHOTO/7cfdcf5ef122422499e4bffa71485ee1 //这里的PHOTO是column family,从下面可以看出,这个column family下一共有3个HFile文件,这里,HFile文件名为7cfdcf5ef122422499e4bffa71485ee1,这个文件是空文件,代表对实际存有数据的同名HFile的一个引用,下个图可以看到
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/395b0d05df155fddbb03b1da908dae3d
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/395b0d05df155fddbb03b1da908dae3d/.regioninfo
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/395b0d05df155fddbb03b1da908dae3d/PHOTO
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/395b0d05df155fddbb03b1da908dae3d/PHOTO/74e4639c360c4cc59806ba48d13ba230
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/87a9a6202d9f68d9ccbd184b19cb8933
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/87a9a6202d9f68d9ccbd184b19cb8933/.regioninfo
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/87a9a6202d9f68d9ccbd184b19cb8933/PHOTO
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot/87a9a6202d9f68d9ccbd184b19cb8933/PHOTO/902eafefe50f4ba2ba01bd80d0846cf8

看看Photo表PHOTO column family下的HFile文件:

bin/hadoop fs -ls -R hdfs://sync/hbase/data/sync_stage/Photo/
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/.tabledesc
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/.tabledesc/.tableinfo.0000000002
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/.tmp
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/0595cf25f61de0f1c3ddf38e50a59b07
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/0595cf25f61de0f1c3ddf38e50a59b07/.regioninfo
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/0595cf25f61de0f1c3ddf38e50a59b07/PHOTO
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/0595cf25f61de0f1c3ddf38e50a59b07/PHOTO/7cfdcf5ef122422499e4bffa71485ee1 //这个HFile存有实际的数据,并且HFile文件名相同
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/395b0d05df155fddbb03b1da908dae3d
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/395b0d05df155fddbb03b1da908dae3d/.regioninfo
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/395b0d05df155fddbb03b1da908dae3d/PHOTO
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/395b0d05df155fddbb03b1da908dae3d/PHOTO/74e4639c360c4cc59806ba48d13ba230
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/87a9a6202d9f68d9ccbd184b19cb8933
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/87a9a6202d9f68d9ccbd184b19cb8933/.regioninfo
drwxr-xr-x - work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/87a9a6202d9f68d9ccbd184b19cb8933/PHOTO
-rw-r--r-- work supergroup -- : hdfs://sync/hbase/data/sync_stage/Photo/87a9a6202d9f68d9ccbd184b19cb8933/PHOTO/902eafefe50f4ba2ba01bd80d0846cf8

下面看看Snapshot的原理。

Snapshot的过程类似于两阶段提交,大体过程是,HMaster收到snapshot命令后,作为coordinator,然后从meta region中取出Photo表的region和对应的region server的信息,这些region server就作为两阶段提交的participant,prepare阶段就相当于对region server本地的Photo表的region做快照存入HDFS的临时目录,commit阶段其实就是HMaster把临时目录改成正确的目录。期间,HMaster和region server的数据共享通过ZK来完成。

下面看Snapshot的具体实现。

在HMaster端,由SnapshotManager类的对象来负责和Snapshot相关的事务,内部有一个类型为ProcedureCoordinator的对象,名为coordinator,从名字可以看出它就是协调者。HMaster收到Snapshot命令,执行public SnapshotResponse snapshot(RpcController controller, SnapshotRequest request)函数,函数内部从request中解析出SnapshotDescription对象,它就是对这次Snapshot的描述,其中就包括Snapshot的名字PhotoSnapshot,和Snapshot的表Photo等。然后调用SnapshotManager的takeSnapshot()方法,方法内部首先会检查Photo表是不是正在做Snapshot,或者名为PhotoSnapshot的snapshot已经做完了等前置检查,如果没有,由于这里做的是online snapshot,即表仍然可以读写处于enable状态,在这里,会调用snapshotEnabledTable(),进而提交一个EnabledTableSnapshotHandler任务给内部线程池处理,在提交之前,也会做一些检查,并且准备好snapshot用的临时目录,在这个例子中,临时目录为hdfs://sync/hbase/.hbase-snapshot/.tmp/PhotoSnapshot,前置检查环境准备等由函数prepareToTakeSnapshot(snapshot)负责。重点看EnabledTableSnapshotHandler,它继承于TakeSnapshotHandler,任务入口函数在TakeSnapshotHandler的process()方法。下面重点看这个方法。

process方法主要干几件事:

1. 将SnapshotDescription对象序列化写入到hdfs://sync/hbase/.hbase-snapshot/.tmp/PhotoSnapshot目录的.snapshotinfo文件中。

2. 调用TableInfoCopyTask任务将Photo表的最新的.tableinfo拷到hdfs://sync/hbase/.hbase-snapshot/.tmp/PhotoSnapshot/.tabledesc/ 目录下,名字为.tableinfo.0000000001。

3. 从meta region中独处Photo表的region和所在region server信息,传给snapshotRegions()函数,该函数被EnabledTableSnapshotHandler覆盖,流程进入EnabledTableSnapshotHandler的 snapshotRegions()。

4. proc.waitForCompleted(); 等待snapshot完成,其实就是等待completedLatch变成0

5. 做一些postcheck ,然后调用completeSnapshot(this.snapshotDir, this.workingDir, this.fs) 将working dir改成正确的目录位置hdfs://sync/hbase/.hbase-snapshot/PhotoSnapshot

下面重点看第三步.EnabledTableSnapshotHandler的snapshotRegions(),这个函数首先调用

Procedure proc = coordinator.startProcedure(this.monitor, this.snapshot.getName(),
this.snapshot.toByteArray(), Lists.newArrayList(regionServers));

启动一个Procedure,在HMaster端,这个Snapshot由一个Procedure来表示,在RegionServer端,有SubProcedure表示,后续会看到。实际上,这里提供了一套框架,以后如果有其他的需要两阶段提交的任务也可以放进来做。

Procedure同样提交给内部线程池处理,Procedure是一个callable,入口函数在call()。call内主要是执行如下几个函数:

sendGlobalBarrierStart(); // 发布Snapshot任务
waitForLatch(acquiredBarrierLatch, monitor, wakeFrequency, "acquired");//等所有的相关的region server都acquire这个任务
sendGlobalBarrierReached(); //建立reached节点
waitForLatch(releasedBarrierLatch, monitor, wakeFrequency, "released"); //等待所有的相关的region server完成本地snapshot
sendGlobalBarrierComplete(); //将zk上相关节点删除
completedLatch.countDown();// proc结束

Procedure有几个关键的成员变量,acquiringMembers 初始化为Photo表的regions所在的serverName,意思是说这个任务需要这些serverName作为参与者,HMaster在ZK上发布Snapshot任务,需要这些参与者都去acquire这个任务后,大家才可以进入下一个阶段。在当前例子,HMaster调用sendGlobalBarrierStart()方法发布任务,方法内部实际上调用coordinator(ProcedureCoordinator类)对象的ZKProcedureCoordinatorRpcs类型成员的sendGlobalBarrierAcquire()方法去ZK上发布Snapshot任务,实际上就是在zk上创建/hbase/online-snapshot/acquired/PhotoSnapshot  路径,并且PhotoSnapshot是一个目录,目录的data为SnapshotDescription的序列化。RegionServer启动的时候会监控/hbase/online-snapshot/acquired目录的改动,当region server在目录下发现一个新的节点后,就会在/hbase/online-snapshot/acquired目录下建立一个代表自己的znode,名字为region server的server name,代表当前region server已经检测到这个任务了。一旦HMaster检测到一个新的znode,会触发coordinator的ZKProcedureUtil类型的名为zkProc的成员变量的nodeCreated()方法,从而调用coordinator的memberAcquiredBarrier()方法,检测,如果新加的节点确实在acquiringMembers内,则将acquiredBarrierLatch这个CountDownLatch减1。这里需要检查新加的节点不在acquiringMembers内的原因在于,实际上,不相关的region server也会acquire这个任务,只是当它发现自己没有相关的region后,直接就执行完成了。所有的acquire成功的server name都会从acquiringMembers移除然后加入到inBarrierMembers中,随后,调用sendGlobalBarrierReached()在zk上创建节点 /hbase/online-snapshot/reached/PhotoSnapshot,并且监控目录下的节点变化,本地snapshot完成的region server会在这个目录下建立一个代表自己的节点,与前面类似,通过releasedBarrierLatch这个CountDownLatch来控制。

下面看看RegionServer检测到/hbase/online-snapshot/acquired下面的snapshot任务后如何做。

RegionServer使用RegionServerSnapshotManager来管理Snapshot相关的事务,主要工作由内部类型为ZKProcedureMemberRpcs

的成员变量memberRpcs来完成,region server初始化时,就会调用ZKProcedureMemberRpcs的waitForNewProcedures()方法来监控zk上/hbase/online-snapshot/acquired下面节点的变化。当检测节点增加后,会调用ProcedureMember的

public Subprocedure createSubprocedure(String opName, byte[] data) {
return builder.buildSubprocedure(opName, data);
}

方法来创建SubProcedure,这里的builder是SnapshotSubprocedureBuilder,它的buildSubprocedure()会创建FlushSnapshotSubprocedure类型的subprocedure,FlushSnapshotSubprocedure有一个名为regions的成员变量,这里会进行初始化,从region server的online regions列表中检查是否有被snapshot表的region,如果有,则初始化regions,否则regions为空。同样,这个subprocedure会提交给内部的线程池处理.FlushSnapshotSubprocedure继承于Subprocedure,它是一个callable,入口函数是call。这个call实际上执行如下几个函数:

acquireBarrier();// 对于FlushSnapshotSubprocedure来说,do nothing
rpcs.sendMemberAcquired(this); //在acquired下建立znode代表自己
waitForReachedGlobalBarrier(); //等在inGlobalBarrier这个CountDownLatch上,初始化为1,只有reached下面相应的snapshot节点建立后(这说明所有相关的re//gion server都已经acquire 任务了)才继续往下走
insideBarrier(); //调用子类FlushSnapshotSubprocedure的insideBarrier
rpcs.sendMemberCompleted(this); //本地snapshot完成后,在reached下建立一个znode代表自己
releasedLocalBarrier.countDown();
executionTimeoutTimer.complete();

可以看出,只有reached相应节点建立,region server才可以往下走进行实际的snapshot操作,而reached节点的建立只有HMaster看到所有的相关的region server都已经acquire了任务后才会去建立,这就达到了同步的目的。

下面看FlushSnapshotSubprocedure的insideBarrier().

对于regions(创建FlushSnapshotSubprocedure的时候进行了初始化,这些regions就是本region server所包含的被snapshot表的region)里的每个region提交一个RegionSnapshotTask类型的任务,然后等待所有的这些task完成。

每个RegionSnapshotTask的任务就是真正的这个region的数据进行snapshot,下面重点看。

1. 调region.flushcache(),转而调internalFlushcache(status)=>internalFlushcache(this.log, -1, status),主要逻辑在internalFlushcache(this.log, -1, status)中。看下面一段:

 this.updatesLock.writeLock().lock();//加写锁,以便冻结region内所有的memstore
long totalFlushableSize = 0;
status.setStatus("Preparing to flush by snapshotting stores");
List<StoreFlushContext> storeFlushCtxs = new ArrayList<StoreFlushContext>(stores.size());
long flushSeqId = -1L;
try {
// Record the mvcc for all transactions in progress.
// 目的是为了后续调用mvcc.waitForRead(w),使得w之前的所有的写事务结束并且可见,以便flush时不会把没有commit的事务flush到HFile中。
w = mvcc.beginMemstoreInsert();
mvcc.advanceMemstore(w);
// check if it is not closing.
if (wal != null) {
if (!wal.startCacheFlush(this.getRegionInfo().getEncodedNameAsBytes())) {
String msg = "Flush will not be started for ["
+ this.getRegionInfo().getEncodedName() + "] - because the WAL is closing.";
status.setStatus(msg);
return new FlushResult(FlushResult.Result.CANNOT_FLUSH, msg);
}
// flush 操作对应的日志的sequence id
flushSeqId = this.sequenceId.incrementAndGet();
} else {
// use the provided sequence Id as WAL is not being used for this flush. flushSeqId = myseqid;
} for (Store s : stores.values()) {
totalFlushableSize += s.getFlushableSize();
storeFlushCtxs.add(s.createFlushContext(flushSeqId));
} // prepare flush (take a snapshot)
for (StoreFlushContext flush : storeFlushCtxs) {
flush.prepare(); // 冻结memstore,后续进行flush到HFile
}
} finally {
this.updatesLock.writeLock().unlock(); //解写锁,可以继续接受写入了
}

然后调用mvcc.waitForRead(w),该函数返回后,那么w之前的所有的写事务都已经结束并且对外可见,后续即可flush。接着,进行实际的flush操作,调用每个

StoreFlushContext的flushCache(),进而会调到HStore的flushCache():

// 对于不同的storeEngine返回的Flusher不一样,默认是DefaultStoreEngine,还可以是StripeStoreEngine,它来源于Compression策略参看(HBASE-7667) 
StoreFlusher flusher = storeEngine.getStoreFlusher();
IOException lastException = null;
for (int i = 0; i < flushRetriesNumber; i++) {
try {
//对memstore进行flush,返回的文件名通过 fs.createTempName()得到,generateUniqueName(null)得到文件名(不包括目录)
//对于DefaultStoreEngine来说,一个memstore会产生一个HFile,StripeStoreEngine会产生几个(HBASE-7667)
List<Path> pathNames = flusher.flushSnapshot(
snapshot, logCacheFlushId, snapshotTimeRangeTracker, flushedSize, status);
Path lastPathName = null;
try {
for (Path pathName : pathNames) {
lastPathName = pathName;
validateStoreFile(pathName);
}
return pathNames;

2. 调region.addRegionToSnapshot(),它主要是将region info写入到snapshot到临时目录中的文件.regioninfo中,然后在临时目录的各个column family文件夹中,创建和存有数据的HFile文件名相同的空文件,代表对实际HFile的引用。

至此,Snapshot结束.

参考资料:

hbase-server-0.98.4-hadoop2.jar

https://issues.apache.org/jira/browse/HBASE-7667

https://issues.apache.org/jira/browse/HBASE-6055

HBase Snapshot原理和实现的更多相关文章

  1. HBase Snapshot简介

    一.简介 HBase 从0.95开始引入了Snapshot,可以对table进行Snapshot,也可以Restore到Snapshot.Snapshot可以在线做,也可以离线做.Snapshot的实 ...

  2. (转)HBase 的原理和设计

    转自:HBase的原理和设计 HBase架构:

  3. HBase Snapshot功能介绍

    HBase在0.94之后提供了Snapshot功能,一个snapshot其实就是一组metadata信息的集合,它可以让管理员将表恢复到以前的一个状态.snapshot并不是一份拷贝,它只是一个文件名 ...

  4. hbase snapshot 表备份/恢复

    snapshot其实就是一组metadata信息的集合,它可以让管理员将表恢复到以前的一个状态.snapshot并不是一份拷贝,它只是一个文件名的列表,并不拷贝数据.一个全的snapshot恢复以为着 ...

  5. Hadoop 综合揭秘——HBase的原理与应用

    前言 现今互联网科技发展日新月异,大数据.云计算.人工智能等技术已经成为前瞻性产品,海量数据和超高并发让传统的 Web2.0 网站有点力不从心,暴露了很多难以克服的问题.为此,Google.Amazo ...

  6. hadoop学习第七天-HBase的原理、安装、shell命令

    一. hbase的原理知识 1. hbase介绍 hbase是hadoop的一个重要成员,主要用于存储结构化数据,构建在hdfs之上的分布式存储系统,它主要通过横向扩展,通用不断增加廉价服务器增加计算 ...

  7. 【HBase】二、HBase实现原理及系统架构

      整个Hadoop生态中大量使用了master-slave的主从式架构,如同HDFS中的namenode和datanode,MapReduce中的JobTracker和TaskTracker,YAR ...

  8. Hbase概念原理扫盲

    一.Hbase简介 1.什么是Hbase Hbase的原型是google的BigTable论文,收到了该论文思想的启发,目前作为hadoop的子项目来开发维护,用于支持结构化的数据存储. Hbase是 ...

  9. HBase 底层原理详解(深度好文,建议收藏)

    HBase简介 HBase 是一个分布式的.面向列的开源数据库.建立在 HDFS 之上.Hbase的名字的来源是 Hadoop database,即 Hadoop 数据库.HBase 的计算和存储能力 ...

随机推荐

  1. 初学版本控制更新Version control

    概述: 在学习计算机软件工程中,修订控制是跟踪和控制源代码更改的任何类型的实践. 对于软件开发人员有时会使用修订控制软件来维护文档和配置文件以及源代码. 当团队设计,开发和部署软件时,通常会将同一软件 ...

  2. PythonStudy——Python 注释规范

    注释规范:   什么是注释?  注释:不会被python解释器解释执行,是提供给开发者阅读代码的提示 单行注释: # 开头的语句 多行注释:出现在文件最上方,用''' '''包裹的语句   Pycha ...

  3. js 判断是否可以打开本地软件

    js判断时候可以打开本地的软件或者插件 点击一个按钮,打开本地的软件,比如问题反馈,需要调起本地的邮箱,填入一些信息. 这个功能<a>标签有提供支持,但是如果本地没有安装邮箱,则无法打开, ...

  4. edit this cookie chrome插件 (HttpAnalyzerStdV3 获取Cookie 后,再用edit this cookie添加cookie)

    插件下载地址及安装方法:http://www.bkill.com/download/154494.html 博客提供下载地址:https://files.cnblogs.com/files/Fooo/ ...

  5. 关于bit,bin文件的一些研究

    关于bit,bin文件的一些研究 bit文件里面有head information 但bin文件里面并不包含 bit 文件里面包含如下信息 SPI flash 时钟需要用到的源语 watchdog 设 ...

  6. mongodb与mysql命令详细对比

    传统的关系数据库一般由数据库(database).表(table).记录(record)三个层次概念组成,MongoDB是由数据库(database).集合(collection).文档对象(docu ...

  7. django操作数据库 ORM

    一,数据库连接 创建类 a. 在models.py中先写类 from django.db import models # 表名是app01_userinfo class UserInfo(models ...

  8. CentOS 7.2 下nginx SSL证书部署的方法(使用crt以及key 配置)

    转自:https://www.jb51.net/article/107350.htm 环境 系统环境:CentOS6.7 nginx version: nginx/1.8.1 证书 ? 1 2 3 # ...

  9. Ubuntu 14.10 下安装Ambari 问题汇总

    在编译安装Ambari时候遇到了很多问题,现在记录一下 1 got error npm ERR! phantomjs@1.9.12 install while building ambari-web ...

  10. (转)EF5+SQLserver2012迁移到EF6+mysql5.5.47

    原文地址:https://www.cnblogs.com/tinyjian/p/6235014.html:https://www.cnblogs.com/tinyjian/p/6235397.html ...