登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux rsync --delete-after 的删除时机与回滚风险

来源:17golang原创

时间:2026-11-01 10:20:41 198浏览 收藏

如果你把部署目录从源端同步到目标端,同时又希望目标端多出来的旧文件在本次传输结束后再清理,rsync --delete-after 就是针对这个时机的选项。它会隐含启用删除逻辑,把删除多余文件安排到传输完成之后;但它不是事务,也没有“失败后自动回滚”能力。源端清单、过滤规则或路径写错时,目标端仍可能在收尾阶段删除不该删除的文件。

官方地址:https://rsync.samba.org/

把 --delete-after 当成“延后执行删除”,不要当成“安全删除”。上线前先用同样的源端、目标端和过滤规则做预演;需要恢复能力时,额外准备 --backup-dir 或文件系统快照。

先看清 --delete-after 的删除时机

rsync 先根据源端构建文件清单,再传输新增或变化的对象。使用 --delete-after 时,目标端不在每个目录开始处理时立即清理,而是在传输完成后做一次删除阶段。这个安排适合“本次传输带来了新的目录级过滤文件,删除阶段需要使用它们”的场景。

它还有一个容易被忽略的代价:官方手册说明,--delete-after 会强制使用旧的非增量递归算法,需要一次性扫描传输涉及的文件。文件数量很大时,内存压力和文件清单构建时间可能明显高于默认的增量删除方式。

rsync delete-after 从源端文件清单到接收端删除阶段的静态结构说明图
图1:传输完成后才进入删除阶段的关系说明图,不是终端截图或运行证据。

从旧命令迁移到可预演命令

假设源端是发布目录,目标端是待切换目录。先用完全相同的参数做 dry-run,重点观察 *deleting 行以及源端路径是否符合预期:

# 中文说明:只预演删除清单,不改动目标端;正式命令需复用同一组参数。
rsync -a --delete-after --dry-run --itemize-changes \
  --backup --backup-dir=/srv/backup/release-2026-11-01 \
  /srv/release/ deploy@example:/srv/app/

上面的命令只预演,不改动目标端。代码块中的反斜杠用于续行;--itemize-changes 让变更类型更容易被日志捕获;--backup-dir 指定一个目标端备份目录,便于把被覆盖或删除的文件移出主目录。正式执行前,至少检查下面三件事:

  • 源端末尾的斜杠是否正确:/srv/release/ 表示同步目录内容,而不是再套一层 release 目录。
  • 目标端是否确实是接收端:命令方向写反时,删除风险会落到另一台机器。
  • 过滤文件、环境文件和备份目录是否被保护:不要把刚生成的备份目录再次纳入删除范围。

预演输出不等于回滚点。它只是按当前时刻计算出来的候选变化;如果源端在正式执行前变化,或者过滤规则来自动态的合并文件,最终删除列表也可能变化。

什么时候改用 --delete-delay

--delete-delay 也会尽量把删除动作放到传输后,但它在传输过程中记录待删除对象,随后统一执行删除。与 --delete-after 相比,它通常更适合保持增量递归的效率;代价是删除名单需要暂存,并且在特定文件数量或临时空间条件下可能回退到其他处理方式。

可以按下面的迁移表判断:

选择删除位置主要取舍适合的判断
--delete-before传输前先释放空间,但会拖延传输开始目标磁盘紧张且能接受先清理
--delete-during按目录增量处理通常更高效,但删除和更新交错目录规模大、无需等到最后清理
--delete-delay传输后集中处理兼顾延后删除和增量递归,需暂存删除名单希望减少误删窗口又不想强制旧递归
--delete-after传输完成后单独处理过滤文件对本次删除生效,但文件清单占用可能更高确实需要“新过滤文件先传过去,再决定删什么”
rsync 三种删除时机与备份和删除上限的静态对比说明图
图2:三种删除时机与安全护栏的概念对比图,属于原创结构图。

给不可逆删除加上回滚边界

rsync 的删除不是数据库事务。即使传输阶段看起来正常,收尾阶段仍可能受到源端目录被误清空、include/exclude 规则过宽、挂载点未就绪或参数方向错误的影响。实际发布可以组合以下护栏:

  1. 备份被覆盖和删除的对象。使用 --backup --backup-dir=/srv/backup/release-2026-11-01,并让备份目录位于同步目标之外或明确加入保护规则。
  2. 限制一次删除的数量。使用 --max-delete=50 作为异常保护;超过上限时,rsync 会跳过后续删除并以错误码结束,不能把它当成成功部署。
  3. 把删除日志单独留存。将 dry-run 和正式运行的 --itemize-changes 输出写入带批次号的日志,方便把删除对象与发布清单对应起来。
  4. 避免用 --ignore-errors 掩盖故障。官方文档默认在发送端检测到 I/O 错误时关闭目标端删除,这是保护机制;只有已经理解风险并有额外恢复手段时才考虑覆盖这个行为。

一个偏保守的正式命令可以这样写:

rsync -a --delete-after --itemize-changes \
  --backup --backup-dir=/srv/backup/release-2026-11-01 \
  --max-delete=50 \
  /srv/release/ deploy@example:/srv/app/ \
  | tee /var/log/rsync-release-2026-11-01.log

# 中文说明:先保留被覆盖或删除的目标文件,再把删除数量限制在 50 个以内。
# 中文说明:命令失败或触发上限时,应停止切换并检查日志,不要直接重跑同一条命令。

这里的备份目录只是文件级恢复手段,不等于整个系统快照。需要保证权限、磁盘空间和保留周期;如果发布目录还涉及数据库、软链接或挂载点,仍应使用与应用架构匹配的快照和回滚方案。

上线前的版本迁移清单

从不带删除、带默认增量删除或旧脚本中的 --delete-before 迁移到 --delete-after,建议按这个顺序收口:

  1. 记录 rsync 两端版本和目标目录的文件数量,确认是否能接受非增量递归的内存开销。
  2. 固定源端、目标端、末尾斜杠和过滤规则,先执行一次 --dry-run。
  3. 对比预演输出,确认待删除文件都在本次发布的淘汰清单内。
  4. 启用 --backup-dir 或文件系统快照,并验证恢复目录有足够空间。
  5. 设置 --max-delete,把异常退出、错误码和日志纳入发布平台的失败条件。
  6. 明确传输完成后的删除阶段由谁观察,避免脚本在删除时被超时或会话断开而无人处理。

最重要的变化不是把删除延后几秒,而是把“源端清单是否完整”和“目标端多余文件是否应该删除”拆成两个需要分别确认的决策。

常见问题

--delete-after 会不会自动回滚?

不会。它只改变删除时机;回滚要靠 --backup-dir、快照、版本化发布目录或其他外部恢复方案。

文件很多时一定要用 --delete-after 吗?

不一定。官方文档说明它会强制旧的非增量递归;如果只是希望最后再删除,可以优先评估 --delete-delay 的内存和临时空间特征。

为什么发送端有 I/O 错误却没有删除目标端文件?

这是 rsync 的默认保护行为,用来避免源端暂时的文件系统故障引发大范围删除。不要为了让脚本“继续完成”就直接加 --ignore-errors。

--max-delete=0 能不能当作正式发布?

它更适合用于只观察目标端多余对象的预演;如果要正式同步,应使用一个符合变更窗口的正数上限,并把触发上限视为失败信号。

参考:https://rsync.samba.org/ftp/rsync/rsync.html

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>