DistCp的快照功能是其一个重要特性,用于保证数据在数据复制过程中的一致性,可以极大降低目录文件扫描时间,提高迁移效率。采用普通distcp迁移hdfs目录时,作业每次执行会全量对比HDFS目录迁移前后的文件,在增量迁移阶段,如果迁移源目录文件量较大、大部分已全量迁移到目标目录、增量数据较少时,这会导致整个迁移过程耗时很长、大部分耗时在文件比对上。而基于快照功能的迁移能够仅拷贝这些有差异的文件,避免扫描目录下全部文件而耗费较长时间,从而减少整个迁移作业的执行时间。
HDFS快照概况
定义
在Hadoop文件系统(HDFS)中,快照是文件系统状态的一个只读镜像。快照可以在任何时间点创建,用于记录文件系统或指定目录在某一刻的状态。快照一旦创建就会保持不变,即使源文件系统发生了改变。快照的一些常见用例包括数据备份、防止用户错误和灾难恢复。
可快照目录
- 当HDFS目录被设置为snapshottable ,就可以在该目录上生成快照。
- 一个可快照目录能够同时容纳 65,536 个快照。
- 一个HDFS里可快照目录的数量没有限制,任何目录设置都为可以设置为可快照目录。
- 如果可快照目录中存在快照,则在删除所有快照之前,既不能删除该目录,也不能重命名该目录。
- 目前可快照目录不允许嵌套。即如果目录的父目录或子目录是可快照目录,则无法将其设置为可快照目录。
存放路径
对于可快照目录,其快照存放在目录下的“.snapshot”路径。常用的 API 和 CLI都可以使用“.snapshot”路径。
示例:假设/foo是一个可快照的目录,/foo/bar是/foo中的文件/目录,并且/foo有一个快照s0。则路径/foo/.snapshot/s0/bar代表/foo/bar的快照s0。
注:在支持快照功能HDFS版本中,.snapshot是个保留名称(reserved name),但是在不支持快照功能的旧版本HDFS中不是,因此当该旧版本升级到新版本时,需要首先重命名或删除名为.snapshot的已存在路径,以避免与新版本的保留路径冲突。
特点
HDFS快照的实现是高效的:
- 快照创建是瞬时的:成本为O(1),不包括 inode 查找时间。
- 仅当相对于快照进行修改时才会使用额外的内存:内存使用量为O(M),其中M是修改的文件/目录的数量。
- datanode中的块不会被复制:快照文件记录块列表和文件大小。没有数据复制。
- 快照不会对常规 HDFS 操作产生不利影响:修改按时间倒序记录,以便可以直接访问当前数据。快照数据是通过从当前数据中减去修改来计算的。
操作
允许快照
切换到root用户下,执行以下命令,可允许指定目录创建快照
hdfs dfsadmin -allowSnapshot <路径>
- <路径>: 允许创建快照目录的路径
禁止快照
切换到root用户下,执行以下命令,可禁止目录创建快照
hdfs dfsadmin -disallowSnapshot <path>
注:如果快照目录包含快照,需要首先删除所有的快照后再禁止快照功能
创建快照
使用目录的所有者或者hdfs超级用户账号来执行创建快照命令,可为一个目录创建快照
hdfs dfs -createSnapshot <path> [<snapshotName>]
删除快照
使用目录的所有者或者hdfs超级用户账号来执行删除快照命令,可删除该目录的指定快照
hdfs dfs -deleteSnapshot <path> <snapshotName>
重命名快照
使用目录的所有者或者hdfs超级用户账号来执行重命名快照命令,可重命名该目录的指定快照
hdfs dfs -renameSnapshot <path> <oldName> <newName>
获取快照目录列表
获取当前用户有权限创建快照的所有快照目录
hdfs lsSnapshottableDir
获取快照差异报告
获取两个快照之间或快照与目录当前状态之间的差异。此操作需要执行用户有两个快照中所有文件/目录的读取访问权限。
hdfs snapshotDiff <path> <fromSnapshot> <toSnapshot>
返回结果符号说明:
符号 | 说明 | 示例 |
---|---|---|
+ | 文件/目录已新增 | + ./text1.txt |
- | 文件/目录已被删除 | - ./text1.txt |
M | 文件/目录已被修改 | M ./foo/bar |
R | 文件/目录已重命名 | R ./foo -> ./foo2 |
DistCp快照拷贝功能
DistCp提供-diff选项,实现将快照之间的差异部分从源集群同步到目标集群。该接口能够复制、重命名和删除快照差异列表中的文件。
使用方法
执行以下命令,将目录从快照<from_snapshot>到快照<to_snapshot>的差异拷贝到
hadoop distcp -update -diff <from_snapshot> <to_snapshot> <source> <destination>
要求:
- 使用-diff时必须包含-update,不能包含-delete
- 目录需要同时包含名为和的快照,不需要为当前状态的快照
- <to_snapshot>创建时间需要晚于<from_snapshot>
- 目录需要包含名为<from_snapshot>的快照,且必须为当前状态的快照
使用示例
测试源目录/src下包含两个快照snap1(旧)和snap2(新),两个快照包含的内容如下
snap1->snap2的差异为新增了./3
测试目标目录/dst为空目录,当前快照为snap1,包含的内容为
运行以下命令,将src目录的snap1->snap2快照差异拷贝到dst
hadoop distcp -update -diff snap1 snap2 /src /dst
查看/dst目录,成功新增了子目录./3
用例测试
测试数据
本地准备4个测试数据文件1.txt、2.txt、3.txt、4.txt
测试环境
在HDFS里创建测试目录/tmp/distcp_test,包含三个子目录,其中src目录为源目录,所有者为hdfs;dst和dst2为目标目录,所有者分别为hdfs和metahouse,目录均开启了快照功能;
首先为src目录创建一个快照,按snap_yyyyMMdd-HHmmss格式命名。
将部分测试数据文件上传到src目录,并在每次变更后,为src目录创建一个快照
总共包含如下4个快照:
测试采用dst和dst2目录为目标目录,目录初始均为空,且不包含快照:
测试内容与结果
源文件的时间、用户名等权限信息是否会拷贝
测试用例 | 结果 |
---|---|
基础命令(只包含-update -diff选项) | 仅同步快照差异数据,不同步权限信息。 |
含-p选项 | 采用-p[rbugpcaxt]命令可同步所有权限信息,其中u(所有者)权限,需要采用超级用户执行命令。 |
基础命令(只包含-update -diff选项)
目标:采用metahouse租户执行基础命令,将src从snap_20240320-193130到snap_20240320-193300的快照差异同步到dst2
为dst2的当前状态创建一个和src同名的快照
hdfs dfs -createSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
将快照差异同步到dst2
hadoop distcp -update -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
查看dst2目录下文件,src目录下文件进行比对
可以看到快照差异已经同步到dst2,但是源文件的权限信息都没有拷贝
含-p命令
目标:采用metahouse租户执行含-p命令,将src从snap_20240320-193130到snap_20240320-193300的快照差异同步到dst2
清空/tmp/distcp_test/dst2下文件
hdfs dfs -rm -r /tmp/distcp_test/dst2/*
# hdfs dfs -deleteSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
# hdfs dfs -createSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
首先采用含-prbugpcaxt命令将快照差异同步到dst2
hadoop distcp -update -prbugpcaxt -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
由于metahouse用户不是超级用户,没有权限修改owner,distcp任务报错。但是快照变更数据依然同步到了dst2目录下,和不添加-prbugpcaxt参数(即步骤a)的同步效果一致
再次清空/tmp/distcp_test/dst2下文件,再次采用含-prbgpcaxt(去掉u)命令将快照差异同步到dst2
hadoop distcp -update -prbgpcaxt -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
任务执行成功,快照差异成功同步,同时保留了除所有者信息外的其他权限信息
同名文件是否会覆盖
测试用例 | 结果 |
---|---|
同名文件内容相同(同一文件) | 内容不会覆盖;权限信息由-p参数单独控制是否修改覆盖,且需要执行租户有修改相关文件的权限 |
同名文件内容相同(不同文件) | 内容不会覆盖;权限信息由-p参数单独控制是否修改覆盖,且需要执行租户有修改相关文件的权限 |
同名文件内容不同 | 会被覆盖;权限信息由-p参数单独控制是否修改覆盖,且需要执行租户有修改相关文件的权限 |
同名文件内容相同(同一文件)
目标:将src从snap_20240320-193130到snap_20240320-193300的快照差异(+1.txt,+2.txt)同步到dst2,重复两次,查看第二次2.txt是否被覆盖
清空/tmp/distcp_test/dst2下文件(如果有),删除dst2已有的snap_20240320-193130快照(如果有),为dst2的当前状态创建一个快照snap_20240320-193130,将快照差异同步到dst2
# hdfs dfs -rm -r /tmp/distcp_test/dst2/*
# hdfs dfs -deleteSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hdfs dfs -createSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hadoop distcp -update -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
运行成功后可以看到快照变更已同步
再次执行一遍相同的命令,dst2删除并生成新快照snap_20240320-193130、将快照差异同步到dst2
结果可以看到,两个文件都跳过了拷贝,并且文件时间等信息都没有改变
同名文件内容相同(不同文件)
目标:dst2目录下创建与src目录下2.txt同名且内容相同的新文件。采用metahouse用户将src从snap_20240320-193130到snap_20240320-193300的快照差异(+1.txt,+2.txt)同步到dst2,查看2.txt是否被覆盖
清空/tmp/distcp_test/dst2下文件(如果有),切换到hdfs用户,创建文件2.txt,将2.txt信息追加进去
# hdfs dfs -rm -r /tmp/distcp_test/dst2/*
hdfs dfs -touchz /tmp/distcp_test/dst2/2.txt
hdfs dfs -appendToFile /tmp/test/2.txt /tmp/distcp_test/dst2/2.txt
hdfs dfs -cat /tmp/distcp_test/dst2/2.txt
删除dst2的snap_20240320-193130快照(如果有),重新为dst2的当前状态创建一个快照snap_20240320-193130,将快照差异同步到dst2
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hdfs dfs -createSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hadoop distcp -update -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
从DistCp Counters里可以看到,有一个文件跳过拷贝,同时dst2目录下2.txt的所有者仍为hdfs,说明2.txt没有被覆盖拷贝
如果加上-prbgpcaxt同步权限执行相同的操作,会出现失败,原因是metahouse用户没有权限修改2.txt(所有者hdfs)
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hdfs dfs -createSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hadoop distcp -update -prbgpcaxt -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
从结果可以看到,1.txt同步成功且权限与src保持一致(除了所有者),但2.txt同步失败,被跳过了拷贝,没有被覆盖
切换到hdfs用户下,再次执行相同的命令,可以看到虽然两个文件都跳过了拷贝,但是权限信息已同步。因此,同名文件内容相同情况下(如2.txt),文件不会覆盖/重复拷贝,权限信息由-p参数控制可单独同步
同名文件内容不同
目标:dst2目录下创建与src目录下2.txt同名且内容不同的新文件,将src从snap_20240320-193130到snap_20240320-193300的快照差异(+1.txt,+2.txt)同步到dst2,查看2.txt是否被覆盖
清空/tmp/distcp_test/dst2下文件(如果有),采用metahouse租户,将4.txt文件上传到dst2目录下,并命名为2.txt
hdfs dfs -put /tmp/test/4.txt /tmp/distcp_test/dst2/2.txt
dst2/2.txt文件和src/2.txt同名但内容不同
为dst2的当前状态创建一个和src同名的快照snap_20240320-193130
# hdfs dfs -deleteSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
hdfs dfs -createSnapshot /tmp/distcp_test/dst2 snap_20240320-193130
将快照差异同步到dst2
hadoop distcp -update -diff snap_20240320-193130 snap_20240320-193300 /tmp/distcp_test/src /tmp/distcp_test/dst2
查看dst2目录下文件,并和src目录下文件进行比对,可以看到快照差异已经同步,并且dst2/2.txt文件内容已经和src/2.txt一致,说明同名文件内容不同会被覆盖
目标目录若存在多余的文件是否会被删除
测试用例 | 结果 |
---|---|
目标目录中包含快照差异报告之外的文件 | 不会删除差异报告之外的文件;如果目标目录不包含差异报告内的文件,程序不会报错 |
目标目录中包含快照差异报告中删除的同名文件 | 会被删除 |
目标目录中包含快照差异报告中删除的同名子目录,子目录下包含多余文件 | 子目录及其包含的所有文件都会被删除 |
目标目录中包含快照差异报告之外的文件
目标:src目录删除2.txt文件并创建新快照,dst目录上传4.txt文件,将src目录-2.txt的快照差异同步到dst目录,查看4.txt文件状态
切换到hdfs用户下,将src目录2.txt文件删除,创建新快照snap_20240321-135650,当前src目录下内容及快照信息如下
确认snap_20240320-193400到snap_20240321-135650的快照差异为-2.txt
hdfs snapshotDiff /tmp/distcp_test/src snap_20240320-193400 snap_20240321-135650
将4.txt文件上传至dst目录,创建快照snap_20240320-193400
hdfs dfs -put /tmp/test/4.txt /tmp/distcp_test/dst
# hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240320-193400
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240320-193400
将src目录snap_20240320-193400到snap_20240321-135650的快照差异同步到dst目录
hadoop distcp -update -diff snap_20240320-193400 snap_20240321-135650 /tmp/distcp_test/src /tmp/distcp_test/dst
从运行结果可以看到,即使dst不存在2.txt文件,程序也可以正常运行;4.txt文件没有被删除,说明快照差异报告之外的文件不会被删除
目标目录中包含快照差异报告中删除的同名文件
目标:src目录删除2.txt文件并创建新快照,dst目录上传4.txt文件命名为2.txt,将src目录-2.txt的快照差异同步到dst目录,查看2.txt文件状态
继续采用a中snap_20240320-193400到snap_20240321-135650的快照差异(- ./2.txt)进行同步测试
hdfs snapshotDiff /tmp/distcp_test/src snap_20240320-193400 snap_20240321-135650
将4.txt文件上传至dst目录,命名为2.txt,删除并重新创建快照snap_20240320-193400
hdfs dfs -put /tmp/test/4.txt /tmp/distcp_test/dst/2.txt
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240320-193400
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240320-193400
将src目录snap_20240320-193400到snap_20240321-135650的快照差异同步到dst目录
hadoop distcp -update -diff snap_20240320-193400 snap_20240321-135650 /tmp/distcp_test/src /tmp/distcp_test/dst
从运行结果可以看到,dst目录下2.txt同名文件被删除,即使dst中的2.txt文件和src中2.txt文件内容不一致
目标目录中包含快照差异报告中删除的同名子目录,该子目录下包含多余文件
目标:dst目录下包含src同名子目录./3和多余文件./3/4.txt,将src目录- ./3的快照差异同步到dst目录,查看4.txt文件状态
src目录删除子目录3(及目录下的3.txt)并创建新快照,dst目录创建3子目录并上传4.txt到子目录3中
hdfs dfs -put /tmp/test/4.txt /tmp/distcp_test/dst/3
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240321-135650
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240321-135650
hdfs snapshotDiff /tmp/distcp_test/src snap_20240321-135650 snap_20240321-142500
将src目录snap_20240321-135650到snap_20240321-142500的快照差异同步到dst目录
hadoop distcp -update -diff snap_20240321-135650 snap_20240321-142500 /tmp/distcp_test/src /tmp/distcp_test/dst
查看结果,发现dst下的子目录./3全部被删除了,包括其中的多余文件4.txt
同步过程中,源目录/目标目录变化是否有影响
测试用例 | 结果 |
---|---|
源目录/目标目录新增文件 | 任务能够正常运行退出 |
源目录/目标目录修改文件 | 任务能够正常运行退出 |
源目录/目标目录新增文件
目标:同步快照差异时,在源目录/目标目录新增文件,查看distcp任务是否能成功运行
# Difference between snapshot snap_20240321-144730 and snapshot snap_20240321-161910 under directory /tmp/distcp_test/src:
# M .
# + ./1GB_file
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240321-144730
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240321-144730
hadoop distcp -update -diff snap_20240321-144730 snap_20240321-161910 /tmp/distcp_test/src /tmp/distcp_test/dst
任务执行后,在源目录/目标目录新增文件5.txt,查看任务是否能成功完成
hdfs dfs -touchz /tmp/distcp_test/dst/5.txt
hdfs dfs -touchz /tmp/distcp_test/src/5.txt
distcp成功完成,新增文件对任务运行没有影响
源目录/目标目录修改文件
目标:同步快照差异时,在源目录/目标目录修改文件,查看distcp任务是否能成功运行
初始状态
待同步的快照差异,其中第2、第3个快照差异(M ./1GB_file)中的具体操作是为1GB_file文件追加了1G的数据
先后提交四个连续的快照差异diff1-4
# Difference between snapshot snap_20240321-165100 and snapshot snap_20240321-190000 under dir
# M .
# + ./1GB_file
# + ./5.txt
# M ./3
# - ./3/1GB_file
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240321-165100
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240321-165100
hadoop distcp -update -diff snap_20240321-165100 snap_20240321-190000 /tmp/distcp_test/src /tmp/distcp_test/dst
# Difference between snapshot snap_20240321-190000 and snapshot snap_20240321-190040 under dir
# M ./1GB_file
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240321-190000
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240321-190000
hadoop distcp -update -diff snap_20240321-190000 snap_20240321-190040 /tmp/distcp_test/src /tmp/distcp_test/dst
# Difference between snapshot snap_20240321-190040 and snapshot snap_20240321-190525 under dir
# M ./1GB_file
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240321-190040
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240321-190040
hadoop distcp -update -diff snap_20240321-190040 snap_20240321-190525 /tmp/distcp_test/src /tmp/distcp_test/dst
# Difference between snapshot snap_20240321-190525 and snapshot snap_20240321-191130 under dir
# M .
# R ./1GB_file -> ./3/1GB_file
# M ./3
hdfs dfs -deleteSnapshot /tmp/distcp_test/dst snap_20240321-190525
hdfs dfs -createSnapshot /tmp/distcp_test/dst snap_20240321-190525
hadoop distcp -update -diff snap_20240321-190525 snap_20240321-191130 /tmp/distcp_test/src /tmp/distcp_test/dst
4个distcp都运行成功,执行完成顺序为diff1、diff2、diff4、diff3,查看结果可以发现最终dsc目录和src目录不一致,表明执行顺序对同步最终结果有影响
注:不采用-diff选项时,运行distcp任务过程中修改拷贝的源文件,会导致任务报错;修改目标文件,不影响任务运行
场景应用:增量数据迁移
distcp的-diff选项能够将两次快照之间的差异操作同步到目标集群,适用于增量迁移的场景
使用步骤
以源目录sourceDir迁移到目标目录targetDir为例
- 第一次迁移采用全量迁移:先在sourceDir创建一个快照s0,利用DistCp拷贝整个目录数据到目标目录targetDir,并在targetDir创建快照s0
- 后续的每次迁移仅迁移增量数据:
- 以每天迁移一次为例,在迁移前先为sourceDir生成一个快照sn,结合前一天迁移生成的快照sn-1,通过-diff选项将两个快照之间的差异操作同步到targetDir
- 迁移完成后在targetDir创建快照sn,并删除前一天迁移生成的快照sn-1,这样就完成了每天增量数据同步
- 等全部迁移工作完成后,删除所有快照
迁移工具解决方案
采用周期策略按天调度,每次调度时自动创建新快照,与上一次调度快照对比,通过distcp同步两次快照之间的差异操作,差异完成后删除上一次调度的快照