snapshot(快照)基本原理快照是很多存储系统和数据库系统都支持的功能。快照是整个文件系统或某个目录在某个时刻的镜像。实现数据文件镜像最简单粗暴的方式就是加锁和复制(之所以需要加锁是因为镜像得到的数据在某一时刻必须是完全一致的数据),不允许原始数据被复制在此期间以任何形式复制。的更新和删除,只提供只读操作,复制完成后释放锁。此方法涉及数据的实际复制。当数据量很大时,必然会花费很多时间。长期锁副本必然会导致客户端长时间无法更新和删除,这在生产线上是不能容忍的。快照机制不复制数据,可以理解为指向原始数据的指针。在HBase的LSM类型系统结构下更容易理解。我们知道,HBase数据文件一旦落盘,将不再允许更新、删除等原地修改操作。如果要更新和删除,可以添加和写入新文件(HBase根本没有更新接口,删除命令也是追加写入)。在这种机制下,要实现对某个表的快照,只需要为当前表中的所有文件创建一个新的引用(指针),为其他新写入的数据创建一个新的文件写入即可。如下图所示:快照过程主要有3个步骤:添加全局锁,此时不允许写入、更新和删除任何数据。分别为所有的HFile文件将Memstore中的缓存数据flush到文件中(可选)新建引用指针,这些指针元数据就是snapshot的扩展思考:LSM系统真的很容易理解,那么其他非LSM系统如何更新in-放置存储系统实现快照?快照可以实现什么功能?快照是HBase功能中非常核心的一部分,使用快照的不同用法可以实现很多功能,例如:全量/增量备份:任何数据库都需要有备份功能来实现数据的高可靠性,快照可以很方便的实现表的在线备份功能,对于在线业务请求影响很小。使用备份数据,用户可以在出现异常时快速回滚到指定的快照点。增量备份会在全量备份的基础上,使用binlog进行周期性的增量备份。场景一:正常情况下,对于重要的业务数据,建议每天至少执行一次快照,以保存数据的快照记录,并定期清理过期的快照,这样如果出现重要的业务错误需要回滚back,可以回滚到上一个快照点。场景二:如果要对集群进行大升级,建议在升级前对重要表进行快照。一旦升级出现异常,可以快速回滚到升级前。2、数据迁移:可以使用ExportSnapshot功能将快照导出到另一个集群,实现数据迁移。场景一:机房在线迁移。通常,数据在A机房。因为A机房没有足够的座位或者机架,所以将整个集群迁移到另一个容量更大的B集群,迁移过程中不能停止服务。基本的迁移思路是使用快照恢复B集群的全量数据,然后使用复制技术增量复制集群A的更新数据,等待两个集群的数据一致,然后重定向客户端向B机房申请,具体步骤请参考:https://www.cloudera.com/documentation/enterprise/5-5-x/topics/cdh_bdr_hbase_replication.html#topic_20_11_7使用场景二:使用快照将表数据导出到HDFS,然后使用Hive\Spark进行离线OLAP分析,比如审计报表、月报表等。hbase快照使用百科快照最常用的命令有snapshot、restore_snapshot、clone_snapshot和ExportSnapshot。快照不涉及数据移动,可以在线完成。hbase>snapshot'sourceTable','snapshotName'恢复指定快照,恢复过程会替换原来的数据,将表恢复到快照点,快照点之后的所有更新都会丢失。需要注意的是,在执行restore_snapshot操作之前,需要禁用原表。hbase>restore_snapshot'snapshotName'根据快照恢复新表。恢复过程不涉及数据移动,可以在几秒钟内完成。很好奇它是怎么做到的,下面来听听分解。hbase>clone_snapshot'snapshotName','tableName'使用ExportSnapshot命令将集群A的快照数据迁移到集群B。ExportSnapshot是HDFS层面的操作,MR会用于数据的并行迁移,所以需要在启用了MR的迁移的机器上执行。HMaster和HRegionServer不参与这个过程,所以不会有额外的内存开销和GC开销。唯一的影响是DN在复制数据时需要额外的带宽和IO负载。ExportSnapshot还设置参数-bandwidth以限制此问题的带宽使用。hbaseorg.apache.hadoop.hbase.snapshot.ExportSnapshot\-snapshotMySnapshot-copy-fromhdfs://srv2:8082/hbase\-copy-tohdfs://srv1:50070/hbase-mappers16-bandwidth1024\hbase快照分布式架构-两阶段提交hbase对指定表执行快照操作。实际上对应表的所有region实际上都在执行快照。因为这些region分布在多个RegionServer上,所以需要一种机制来保证所有参与快照执行的region要么完成要么没有开始,不能出现中间状态,比如有的region已经完成,有的region没有完成..HBase使用两阶段提交协议(2PC)来保证快照的分布式原子性。2PC一般由一个协调者和多个参与者组成。整个事务提交分为两个阶段:准备阶段和提交阶段。在prepare阶段,协调器会向所有参与者发送prepare命令,所有参与者开始获取相应的资源(如锁资源)并执行prepare操作,确认是否可以执行成功。通常,核心工作在准备操作中完成。并将准备好的响应返回给协调器。协调器收到所有参与者返回的preparedresponse(表示所有参与者都准备提交)后,在本地持久化提交状态,进入提交阶段。协调者向所有参与者发送提交命令,参与者收到提交命令后,将执行提交操作并释放资源。通常,提交操作非常简单。下面我们就来看看hbase是如何使用2PC协议来构建快照架构的。基本步骤如下:1.Prepare阶段:HMaster在zookeeper中创建一个'/acquired-snapshotname'节点,并在该节点上写入快照相关信息(快照表信息)。所有regionserver监控到该节点后,根据/acquired-snapshotname节点携带的snapshot表信息,查看当前regionserver上是否存在目标表。如果不是,请忽略此命令。如果存在,则遍历目标表中的所有region,对每个region分别进行快照操作。请注意,快照操作的结果不会写入最终文件夹,而是写入临时文件夹。regionserver执行后会在/acquired-snapshotname节点下创建一个新的子节点/acquired-snapshotname/nodex,表示nodex节点已经完成了regionserver上所有相关region的快照准备。2.Commit阶段:当所有regionserver都完成了快照准备工作,即在/acquired-snapshotname节点下创建了相应的子节点,hmaster认为快照准备工作完全完成。master会创建一个新的节点/reached-snapshotname,这意味着向参与的regionservers发送一个commit命令。所有regionserver监控到/reached-snapshotname节点后,执行快照提交操作。commit操作非常简单,只需要将prepare阶段产生的结果从临时文件夹移动到final文件夹即可。执行完成后,在/reached-snapshotname节点下新建一个子节点/reached-snapshotname/nodex,表示节点nodex已经完成了快照工作。3.Abort阶段:如果一定时间内/acquired-snapshotname节点数不满足条件(且regionserver的准备工作还没有完成),hmaster认为snapshot的准备工作超时。hmaster会再创建一个新的节点/abort-snapshotname,所有regionservers都会在监听到这个命令后清理临时文件夹中snapshot产生的结果。可以看出,在这个系统中,HMaster作为协调者,RegionServer作为参与者。HMaster和RegionServer之间的通信是通过Zookeeper完成的,事务状态也记录在Zookeeper上的节点上。当HMaster高可用时,主HMaster宕机。HMaster切入master后,可以根据Zookeeper上的状态决定事务是继续提交还是中止。SnapshotCoreImplementation上一节从架构层面介绍了快照如何在分布式系统中进行原子操作。那么每个区域具体是如何实现快照的呢?hmaster是如何汇总所有region快照结果的?区域如何实现快照?在基本原理一节中,我们提到快照实际上并不复制数据,而是使用指针引用创建的。一组元数据。元数据是什么样的元数据?其实整个snapshot的过程基本是这样的:对应debug日志中的如下片段:snapshot.FlushSnapshotSubprocedure:FlushSnapshottingregionyixin:yunxin,user1359,1502949275629.77f4ac61c4db0be9075669726f3b72e6.started...snapshotest.SnaStoring'yixin:yunxin,user1359,user1359,user1359,1502949275629.77f4ac61c4db0be9075669726f3b72e6.started...snapshotest.SnaStoring'yixin:yunxin,user13591502949275629.77f4ac61c4db0be9075669726f3b72e6.'region-infoforsnapshot.snapshot.SnapshotManifest:Creatingreferencesforhfilessnapshot.SnapshotManifest:Addingsnapshotreferencesfor[]hfiles注意:region生成的snapshot文件是临时文件,生成目录在/hbase/.hbase-snapshot/Under.tmp,generallybecausethesnapshot过程很快,很难看到单个region生成的快照文件。hmaster如何汇总所有region快照的结果呢?hmaster会在所有region快照完成后进行一次汇总操作(consolidate),将所有region快照manifests汇总成一个manifest。在HDFS目录下可以看到汇总的快照文件,路径为:/hbase/.hbase-snapshot/snapshotname/data.manifest。注意snapshot目录下有3个文件,如下图:.snapshotinfo是快照的基本信息,包括要快照的表名和快照名称;data.manifest是快照执行后产生的元数据信息,即快照结果信息。可以使用hadoopdfs-cat/hbase/.hbase-snapshot/snapshotname/data.manifest查看:clone_snapshot如何实现?前面说了snapshot可以用来干很多大事,比如restore_snapshot、clone_snapshot和exportsnapshot等等,这一节我们就来看看clone_snapshot的功能是如何实现的。直接进入正题,整个步骤可以概括为:预检查:确保目标表没有被快照或恢复,否则会直接返回错误。在tmp文件夹下新建一个table目录,在table目录下新建一个.tabledesc文件。在新region目录中写入tableschema信息:这一步是clone_snapshot和createtable最大的区别。新创建的区域目录是根据快照清单中的信息确定的。该区域有哪些列族?列族中有哪些HFile文件?来源于这里。这里有意思的是,clone_snapshot克隆一张表的过程并没有涉及到数据的移动,所以不禁要问克隆出来的表里面是什么文件?如何与原表中的数据文件建立对应关系?该问题的解决方法与拆分过程中参考文件的解决方法基本相同。不过在clone_snapshot中不叫referencefile,而是linkfile。与参考文件不同,链接文件没有内容,只有文件名。例如原文件名为abc,则生成的链接文件为:table=region-abc。这样就可以轻松定位到原始表中原始文件的具体路径:xxx/table/region/hfile,这样就不需要移动数据了。上图中LinkFile文件名为music=5e54d8620eae123761e5290e618d556b-f928e045bb1e41ecbef6fc28ec2d5712,根据定义我们知道music为原始文件的表名,5e54d8620eae123761e5290e618d556b为引用文件所在的region,f928e045bb1e41ecbef6fc28ec2d5712为引用文件,如下图所示:我们可以依据规则可以直接根据ThefilenameoftheLinkFilelocatesthelocationofthereferencefile:***/music/5e54d8620eae123761e5290e618d556b/cf/f928e045bb1e41ecbef6fc28ec2d5712,asshowninthefigurebelow:4.Movethetabledirectoryfromthetmpfoldertothehbaserootlocation5.ModifythemetatableandcloneAddtheregioninformationofthetabletothemetatable.Notethattheregionnameoftheclonedtableisdifferentfromtheregionnameoftheoriginaldatatable(theregionnameisrelatedtothetablename,andifthetablenameisdifferent,theregionnamewilldefinitelynotbethesame)6.PuttheseTheregionisimmediatelyandevenlydistributedtotheentireclusterthroughtheround-robinmethod,andthestatusoftheclonedtableissettoenabledonzk,andtheserviceisofficiallyprovidedtotheoutsideworld.Idon’tknowifyouhavepaidattentiontoanotherissue,accordingtotheaboveWeknowthatsnapshotisactuallyaseriesofmetadataoftheoriginaltable,mainlyincludingtableschemainformation,regioninfoinformationofallregionsoftheoriginaltable,columnfamilyinformationcontainedintheregion,andallhfilefilenamesandfilesizesundertheregion.那么如果对原表进行compaction,导致hfile文件名发生变化或者region分裂,甚至删除原表,之前的快照会不会失效呢?从功能实现的角度来说,肯定不会让用户任何时候点的快照失效,那么在上面列举的各种情况下,如何避免快照失效呢?HBase的实现也比较简单。在对原表进行compact操作之前,会将原表复制到归档目录,然后进行compact(对于表的删除操作,一般会将删除的表数据移动到归档目录),使得快照对应的元数据不会失去意义,只是原来的数据已经不在数据目录中了,而是被移到了归档目录中。可以这样做一个实验:使用snapshot对一个表进行快照,比如snapshot'test','test_snapshot'查看archive目录,确认没有目录:/hbase-root-dir/archive/data/default/test对表test执行major_compact操作:major_compact'test'再次查看归档目录,会发现原来的test表已经移动到该目录下,同样的原因也会存在于/hbase-根目录/存档/数据/默认/测试。原表执行删除操作,如delete'test',也会在归档目录中找到该目录。与删除普通表不同的是,删除普通表后,首先在归档中可以看到被删除表的数据文件,但等待一段时间后,归档中的数据就会被完全删除。已删除,无法再找回。这是因为master上会启动一个定时清理archive中垃圾文件的线程(HFileCleaner),定时清理这些被删除的垃圾文件。但是,原来的快照表被删除入库后,就不能定期清理了。如上所述,克隆的新表没有克隆的实际文件,而是生成的原始文件的链接。这种类型的文件称为LinkFile,显然,只要LinkFile还指向这些原始文件,它们就不能被删除。那么,这里有两个问题:1.LinkFile什么时候才能成为真正的数据文件?如果你看过作者的上一篇文章《HBase原理 – 所有Region切分的细节都在这里了》,你一定对这道题不陌生。是的,HBase中的region被拆分成两个sub-region后,sub-region中的文件也是reference文件。这些引用文件实际上是在执行压缩时从父区域中的文件迁移到它们自己的文件目录中的。LinkFile也是如此。合并后的文件将写入新目录,并在压缩克隆中的新表时删除相关的LinkFile。理论上,这也是通过compact的方式来完成的。2、系统在删除archive中的原始表文件时,如何知道这些文件还被某些LinkFile引用?HBaseSplit后,系统要删除parentregion的数据文件。它必须首先确认在两个子区域中没有指向它的引用文件。系统如何确认这一点?上一节我们分析过,meta表会存储parentregion对应的两个childregion,然后扫描这两个childregion中的所有文件,确认是否有reference文件。如果没有参考文件,就可以安全地删除父区域的数据文件。当然,如果还有参考文件,那就只能作罢了。clone后删除原表文件的方法是一样的吗?答案是否定的,HBase使用了另一种方式来根据原始表文件找到引用文件,这就是反向引用机制。HBase系统在归档目录下新建一个反向引用文件,帮助原表文件找到引用文件。我们先看看回溯文件是一个什么样的文件,它是如何根据原文件定位LinkFile的:(1)原文件:/hbase/data/table-x/region-x/cf/file-x(2)clone生成的LinkFile:/hbase/data/table-cloned/region-y/cf/{table-x}-{region-x}-{file-x},所以很容易根据LinkFile文件(3)回溯文件定位到原来的:/hbase/.archive/data/table-x/region-x/cf/.links-file-x/{region-y}.{table-cloned},可以看到,回溯文件路径中包含了所有的原文件和LinkFile信息,因此可以根据原文件/table-x/region-x/cf/file-x有效定位LinkFile:/table-cloned/region-y/cf/{table-x}-{region-x}-{file-x}这里有兴趣的童鞋可以把这个知识点串起来做个简单的实验:(1)使用snapshot来makeatableSnapshot,suchassnapshot'table-x','table-x-snapshot'(2)使用clone_snapshot克隆一张新表,如clone_snapshot'table-x-snapshot','table-x-cloned'。并查看新表test_clone的HDFS文件目录,确认会有LinkFile(3)删除原表table-x(删除表前确认archive中没有原表文件),查看确认原始表文件进入存档并存在于存档反向引用文件中。注意反向引用文件格式。(4)对表'table-x-clone'执行major_compact,命令为major_compact'test_clone'。执行命令前,确认表x克隆文件目录下存在LinkFile。(5)执行完major_compact后,查看table-x-clone的HDFS文件目录,确认所有的LinkFile都不存在了,都变成了真正的数据文件。
