Apache Hadoop — 在一个 Pod 里搭起并运维 HDFS 与 YARN
找回已删除文件的两条路——回收站与快照
一句话总结
在 HDFS 中找回被删除的数据有两条路。回收站是 shell 不直接删除而是先挪走的客户端装置,默认处于关闭状态。快照是 NameNode 把目录的某个时间点整体保留下来的装置,所以连用 -skipTrash 删除的内容也能恢复。代价是:被快照占着的块,即使删除也不会归还空间。
为什么需要两条路
在分布式文件系统里,失误造成的损失很大。敲错一个路径的 rm -r 会删掉几 TB,出错的作业会覆盖昨天的结果。如果是本地磁盘,从备份里取回就行,但 HDFS 的数据往往大到无法备份。
因此 HDFS 设置了性质不同的两种装置。一种是针对人的手滑的轻量装置:把用 shell 删除的文件暂时保管到别处。另一种是留下目录过去状态本身的装置:无论谁通过什么途径删除或覆盖,都能回到那个时间点。两者运作的位置、防护的范围和付出的代价都不一样。
工作原理
先说回收站。设计文档中关于空间回收的部分说明,如果开启了回收站配置,用 FS shell 删除的文件不会被立刻删掉,而是移到回收站目录。每个用户都有 /user/<이름>/.Trash(占位符为用户名),刚删除的文件会以原来的路径原样放进 .Trash/Current 之下。每隔固定的时间,Current 会被改成以日期命名的检查点,超过保留期的检查点会被删除。到那时 NameNode 才会删掉名字,块才被释放。
回收站的保留期由 core-default.xml 中的 fs.trash.interval 决定,单位是分钟。默认值是 0,0 表示关闭回收站。只安装而没有改动配置的集群,rm 就是立即删除。本课程的实验镜像把这个值设为 1440 分钟,也就是一天。这个值可以同时设在服务端和客户端,服务端开启时使用服务端的值,忽略客户端的值。只有服务端关闭时,才会看客户端的配置。
这也显示出这个装置的性质。回收站是移动而不是删除,而且是由 shell 来做这个决定。shell 文档对 rm 的说明也写道,开启回收站时,文件会被移到回收站目录。所以一旦加上 -skipTrash,就会立即被删除;对于不经过 shell、直接用文件系统 API 删除的程序,最好不要假设有这道安全网。覆盖写入它同样防不住。回收站只保管“被删除的文件”。
快照由 NameNode 持有
快照文档把快照定义为文件系统的只读时间点拷贝。不必被“拷贝”这个词吓到,文档指出的实现特性才是关键。
- 创建的开销是 O(1),立即完成。
- 不会复制 DataNode 上的块。快照只记录文件的块列表和大小。
- 额外内存只用于快照之后发生变化的部分。对于发生变化的文件和目录数 M,开销是 O(M)。
- 变化的内容按时间倒序记录,所以当前数据直接读取即可。读取快照一侧时,从当前数据中减去之后的变化来计算。
所以,快照是 NameNode 的元数据装置。即使删除了文件,快照仍然持有它的块列表,块就不会被释放。因此用 -skipTrash 删除的文件也能恢复。
hdfs dfsadmin -allowSnapshot /data/sales # 관리자: 스냅샷을 허용
hdfs dfs -createSnapshot /data/sales s1 # 소유자: 한 시점을 찍는다
hdfs dfs -rm -skipTrash /data/sales/2026-09.csv # 휴지통을 건너뛴 삭제
hdfs dfs -cp -ptopax /data/sales/.snapshot/s1/2026-09.csv /data/sales/
hdfs snapshotDiff /data/sales s1 . # s1 과 지금의 차이
快照通过名为 .snapshot 的保留路径,以名字读取。恢复就是从这个路径复制。文档中的示例用 -ptopax,一并保留时间、所有者、权限、ACL 和扩展属性。snapshotDiff 会以 +(新增)、-(被删除)、M(被修改)、R(被重命名)显示两个快照之间、或快照与当前(.)之间的差异。移到快照目录之外的内容会显示为被删除,从外面进来的内容会显示为新增。
如果创建快照时不指定名称,就会像 s20130412-151029.033 这样,以创建时间命名。哪些目录可以做快照,用 hdfs lsSnapshottableDir 查看,某个目录的快照列表用 hdfs lsSnapshot 查看。如果定期创建,把日期放进名字里,运行保留策略会更方便。
限制也很明确。允许快照是超级用户的事,创建、删除、重命名则是该目录所有者的事。已允许快照的目录,其祖先或后代不能再次允许(禁止嵌套)。一个目录最多可同时保留 65,536 个快照,hdfs-default.xml 中 dfs.namenode.snapshot.max.limit 的默认值也是 65536。而且,有快照残留的目录不能删除,也不能重命名。必须先删除全部快照。
把两条路放在一起看
| 回收站 | 快照 | |
|---|---|---|
| 在哪里 | 客户端(shell)来移动 | NameNode 以元数据方式持有 |
| 默认状态 | 关闭(fs.trash.interval 为 0) |
必须逐个目录开启 |
| 能防住的 | 用 shell 删除的文件 | 删除、覆盖、重命名都能防 |
| 防不住的 | -skipTrash、覆盖 |
创建快照之前的修改 |
| 空间 | 超过保留期后释放 | 在删除快照之前一直占着 |
在现场相遇的样子
第一,先确认回收站有没有开启。默认值是 0,新建的集群大多是关闭的。如果 rm 之后没有出现“已移到回收站”的消息,那就是立即删除。
第二,快照会占着空间。被删除文件的块由快照持有,所以即使删除,可用空间也不会增加。如 shell 文档所述,不加 -x 时,count 会把快照里的内容也算进去。需要同时有保留策略,定期删除旧快照。
第三,快照差异是增量复制的原料。DistCp 文档中的 -diff 与 -update 配合使用,根据两个快照的差异报告,找出源端和目标端的差别并应用到目标端。不必每次都扫描全部内容,只移动发生变化的部分。
第四,创建过快照的目录,清理时会被拦住。项目结束想删除目录却失败时,先看还剩下哪些快照。
实际工作中真正重要的事
- 回收站是 shell 的功能,默认关闭。
fs.trash.interval为 0 时,rm就是立即删除。 - 快照不复制块。创建的开销是 O(1),开销只用于发生变化的部分。
- 恢复就是从
.snapshot中复制。用-ptopax连权限和 ACL 一起保留。 - 快照会占着被删除文件的空间。要确定保留期限,并删除旧的快照。
- 有快照残留的目录不能删除或重命名。
下一项实验要做什么
先允许目录使用快照并创建第一个快照,然后依次体验:用普通 rm 删除的文件进入回收站(本实验镜像把回收站开启为一天),以及绕过回收站删除的文件,从快照中复制回来。在文件末尾追加一行并创建第二个快照,用 snapshotDiff 读出发生了什么变化。确认想整体删除一个还有快照的目录时会被拒绝,再重命名第二个快照,最后把回收站和快照的差别整理成报告。