HBase自从0.94.6开始加入了Snapshot功能,原则是是尽可能的避免少的网络、磁盘开销而达到对于当前Table的Snapshot以及之后的Restore或者Clone.

一 以下是对Snapshot整个流程的初步整理:

SnapshotManager 在Master存在一个,控制所有Snapshots

Snapshot流程

1. HBaseAdmin.takeSnapshotAsyncRPC -> HMasterInterface.snapshot RPC请求Master
1.1 checkSnapshotSupport ,hbase-site是否支持
1.2 get the snapshot information 获取SnapshotDescription(Proto)
1.3 Check isSnapshotCompleted 是否已经存在这个Snapshot的版本,通过Hdfs上是否存在此文件夹路径判断
1.4 Check 需要被snapshot的表是否存在,通过FSTableDescriptors 判断
1.5 call pre coproc hook MasterCoprocessorHost.preSnapshot 通过coprocessor,预处理pre_snapshot事件
1.6 prepareToTakeSnapshot 确保同时只有一个snapshot在运行
1.7 通过Zookeeper保持的状态来确定该Table的状态
{
如果是enable,启动分布式的snapshot
如果是disable,只在Master上完成snapshot
}
1.8 SnapshotManager.snapshotEnabledTable 实例化EnabledTableSnapshotHandler
{
1.8.1 实例化 MasterSnapshotVerifier 对于Table状态确认的最后一道防线(如果在snapshot的时候发生了split等)
1.8.2 获取coordinator (ProcedureCoordinator)
}
1.9异步启动TakeSnapshotHandler.process()线程处理
{
.tmp文件
目录结构
.snapshot/.tmp/{snapshotName}/.tmp
.snapshot/.tmp/{snapshotName}/.snapshotinfo
.snapshot/.tmp/{snapshotName}/.tableinfo.0000000001
1.9.1 writeSnapshotInfo 在.snapshot/.tmp/{snapshotName}/.snapshotinfo 中写 snapshot description
1.9.2 TableInfoCopyTask.call copy表信息
1.9.2.1 writeTableDescriptor 在.snapshot/.tmp/{snapshotName}/tmp/.tableinfo.0000000001 中写入文件描述信息
1.9.2.2 将.tableinfo.0000000001 移出tmp到.snapshot/.tmp/{snapshotName}/ 下

1.9.3 通过CatalogTracker 扫.META.表获取 regionsAndLocations
1.9.4 EnabledTableSnapshotHandler.snapshotRegions 执行各个Region的snapshot
其中,由ProcedureCoordinator来调度所有的Procedure see http://140.211.11.40/r/8240/diff/2-4/
Proceduer.java.call() 执行一个 barriered procedure
通过zk 建立 /hbase/online-snapshot/reached/v2/3h_william-PC,47055,1370882110005 Node

Procedure.sendGlobalBarrierStart 建立Acquired节点
Procedure.sendGlobalBarrierReached 建立reached节点
1.9.5 RegionServer 的 Zookeeper Watch收到要处理的Procedure简历subProducer
1.9.6 调用FlushSnapshotSubprocedure
1. startRegionOperation 获取各种锁
2. flushcache 将Memstore flush到磁盘
3. 将HFile的文件名写入

}
1.10 snapshot post处理


二 我们已经对对Table进行了Snapshot,那源码中是怎么Clone这个Table呢?


clonesnapshot

Client
1. 构建HSnapshotDescription(里面是protofub对象) Writable对象
2. HMaster.restoreSnapshot(HSnapshotDescription)

Server
1. HMaster.restoreSnapshot
1. CheckSnapshot support
2. get snapshotDir = file:/E:/tmp/hadoop/data/.snapshot/v1
3. read snapshot information 通过.snapshot的文件,读取信息:
1. SnapshotDescription 通过解析(.snapshotinfo)
2. HTableDescriptor   通过解析 (.tableinfo)
4. stop tracking completed restores 将SnapshotManager的restoreHandlers Map,清空
5. CloneTableSchema -> HTableDescriptor 只替换需要clone table的表名
6. preCloneSnapshot
1. check ,确保同一时间只能对于同一个table进行clone
2. CloneSnapshotHandler 构建 处理对象
1. CreateTableHandler 创建表
1. 在 {HBaseDir}/.tmp中创建基本信息
2. CloneSnapshotHandler.handleCreateHdfsRegions 将Snapshot的信息Copy到创建的表中
1. 获取snapshotDir
2. RestoreSnapshotHelper.restoreHdfsRegions()
1. 获取snapshotRegionNames
2. 构建RestoreMetaChanges
3. 判断已存在的HRegin下是否有文件,即是重新回复还是新建
4. cloneHdfsRegions
1. 重新构建所有 ReginName
2. ModifyRegionUtils.createRegions
2. 多线程对每个Region写入磁盘
1.RegionFillTask.fillRegion
其中RestoreSnapshotHelper.restoreStoreFile 触发HFileLink.create 对HFile和Snapshot中的文件对应创建link william=d5f30b31d1a0554d2615a220aebc4e0f-9ffae60195cb45a18519f2a21cf9b285
其中会在.archive中建立link。 E:\tmp\hadoop\data\.archive\william\d5f30b31d1a0554d2615a220aebc4e0f\f1\.links-9ffae60195cb45a18519f2a21cf9b285\9ba86ddb59d34942e2f21a27b5cc2399.william11
7. postCloneSnapshot

2. 新的HRegion加载HStore时候
1. StoreFile.StoreFile 加载StoreFile
1.if (HFileLink.isHFileLink(p)) 判断HFile是link文件
2. 将可能的link具体位置 /.tmp 或 /.archive 或 originPath 列出
3. 加载

Q: 这里有个问题是,如果先做了snapshot,再clonetable,那么这个clone的table是一个指向引用,指向原有table,如果这个时候对原有的table进行split,则需要将原有table的HFile归档到.archive中,这个时候HRegion所加载的变不能释放

KeyClass
HFileLink


三 我们基本得知,Snapshot的思路是对于原来文件HFile的一个Reference的控制,但如果说在Reference情况下,发生了Split,那源码中又是怎么处理的呢?

当已经snapshot后,触发split/compact的处理机制

1. split 触发对于两个daughter Region的compact
1. CompactionRequest.run() 每一个Daughter HRegion各自启线程并发处理
1 . HRegion.compact(CompactRequest)
1. Store.compact majoy compact
1. Compactor.compact
1. Reader = HalfStoreFileReader
2. Scan and Write to DaughterRegion/.tmp 将Reference所指向的部分,写入到tmp中
3. Store.completeCompaction
1. 将DaughterRegion/.tmp 下的HFile移动到DaughterRegion/{ColumnFamily}/下 此时,已经将top部分写完,但Reference部分还在
2. HFileArchiver.archiveStoreFiles 因为需要将原来的Compact文件一出掉,所以要进行归档处理
3. 在 {HBasedir}/.archive下建立需要存档的文件名,即{DaughterRegionName}
4. 将需要归档的文件归档到{HBasedir}/.archive/{tablename}/下 (目前是Reference文件)


Key class
1. HMaster.HFileCleaner 定期清理.archive下的文件
其中 [HFileLinkCleaner, SnapshotHFileCleaner, TimeToLiveHFileCleaner]会对SnapshotHFileCleaner进行排除
2 HMaster.CatalogJanitor 定期清理Parent Region,扫Meta表,如果获取的Parent Region,其Daughters没有Reference文件,则可以被清理,清理到.archive中
3. StoreFile getMaxSequenceIdInList