技术复盘

FastDFS 存储迁移到 NFS 的注意事项与回滚建议

这篇文章来自一次真实的迁移事故:原本双节点正常运行的 FastDFS,在一台节点把本地盘切到 NFS 后,出现了 200 有文件、201 没有文件 的副本不一致现象。真正需要解决的,不只是“文件怎么补回来”,而是如何避免把不完整副本重新暴露给业务。

适用场景:FastDFS 存储迁移 / NFS 切换 结论类型:复盘 + 排障 + 操作建议

问题现象

`172.17.57.201` 把 `/fastdfs` 从本地盘切到 NFS 后,恢复 storage 实例运行,随后发现业务文件出现 200 节点有、201 节点没有 的情况。

结论先看

应先把 200 视为权威源,下线 201 上受影响 group 的 storage,只对真实文件目录补数据,不要整目录覆盖 FastDFS 运行状态目录。

背景

实际迁移过程大致如下:

  • `172.17.57.200` 与 `172.17.57.201` 原本都正常运行 FastDFS。
  • `201` 原先将本地磁盘挂载到 /fastdfs,其中承载了 4 个 group 和 1 个 tracker。
  • 后来新挂载了一块 NFS 到 /mnt/fastdfs
  • 先把原 /fastdfs 下的文件拷贝到 /mnt/fastdfs
  • 再停止 `201` 上的 tracker 和若干 storage,卸载原 /fastdfs,把 NFS 改挂到 /fastdfs 后重新启动服务。
  • 恢复后发现部分文件只存在于 `200`,而 `201` 上缺失。

这个问题的关键不在于“某个服务有没有启动起来”,而在于这次迁移是否真的拿到了一个静态一致的数据视图,以及恢复上线时是否让一个数据不完整的副本提前接入了下载流量

核心判断

一旦出现“200 有、201 没有”,就说明当前受影响 group 的事实源应当优先视为 `200`。此时最忌讳的做法,是让 `201` 继续保持在线并参与下载分发,因为这会把“后台数据不一致”直接升级成“前台业务偶发报错”。

为什么迁移后容易出现副本不一致

  1. 1 拷贝时源目录仍在变化

    如果在 `201` 仍然在线提供服务时执行了 cprsync,那么复制期间 /fastdfs 下仍可能发生新文件写入、同步线程补数、元数据变化。复制到 NFS 的数据,本质上只是某个时刻的近似快照,而不是严格冻结后的最终数据。

  2. 2 FastDFS 本身是异步复制

    FastDFS 同组 storage 一般是“先写源节点,再由副本异步同步”。所以“上传成功”并不等于另一台副本已经完整落盘。只要迁移正好发生在同步窗口内,就有机会留下尾差。

  3. 3 不完整副本恢复上线过早

    如果 `201` 上某个 group 的 storage 已经恢复为 `ACTIVE`,tracker 就可能把下载请求分给它。此时一旦命中缺失文件,业务侧就会看到 file not exist,表现为偶发失败。

一句话理解这次事故

这不是单纯的“迁移后漏了几个文件”,而是一次典型的运行中复制 + 异步副本 + 恢复过早上线叠加问题。

哪些目录能 rsync,哪些绝对不能 rsync

这一步是 FastDFS 迁移中最容易踩坑的地方。目标不是“把 200 整台 storage 克隆成 201”,而是:把 201 缺失的真实文件补齐,同时保留 201 自己的运行身份和状态

可以 rsync 的目录

只限于各 group 的 实际文件存储目录,也就是 store_path 下真正保存文件实体的目录树,例如:

/fastdfs/ebidding/data/
/fastdfs/encryption/data/
/fastdfs/grouptrade/data/
# 或者
/fastdfs/store_path0/data/
/fastdfs/store_path1/data/

这里面放的是文件本体,以及与文件本体同目录的附属小文件,这些可以作为补数据对象同步。

绝对不能直接 rsync 的目录

以下内容不应从 `200` 覆盖到 `201`:

  • tracker 数据目录
  • storage 的 base_path 运行状态目录
  • sync / binlog / *.mark / *.dat
  • 配置文件、日志、pid、sock、lock 等临时或状态文件

最实用的判断法

如果拿不准某个目录能不能同步,只问一句:这里面存的是用户文件本体,还是FastDFS 程序自己的运行状态?前者可以补,后者不要碰。

推荐的迁移操作顺序

  1. 1 迁移前先划清目录边界

    必须明确每个 storage 实例的 base_pathstore_path_count、每个 store_path 实际指向哪里,以及每个 group 对应哪个端口与实例。不要对整个 /fastdfs 一把梭。

  2. 2 第一次复制只当预同步

    第一轮 rsync 只能解决大部分历史数据搬运,不能被视为最终一致结果。它的作用是先降低总数据量,不是替代最终切换。

  3. 3 最终切换前停对应 storage

    最稳妥的流程应是:停止目标 group 的 storage → 执行最终增量同步 → 切换挂载 → 再恢复 storage。这样可以把迁移窗口压到最小。

  4. 4 高写入 group 最好安排短暂停写

    如果业务写入很频繁,即使做了两轮 rsync,也可能在最后恢复前留下尾差。最稳妥的做法,是在最终增量同步前短暂停写几分钟。

  5. 5 不要先恢复全部实例再观察

    如果怀疑某个 group 数据不完整,就应先保持该 storage 离线,等补齐、抽查、确认可读后再上线,而不是让业务流量替你试错。

这次迁移最容易误用的命令粒度

危险做法

直接把整个 /fastdfs 当成可以无差别复制的目录。

rsync -a /fastdfs/ root@201:/fastdfs/

更合理的做法

只同步受影响 group 的真实文件目录;如果是多 store_path,也只同步具体 data 目录。

rsync -a /fastdfs/ebidding/data/ root@201:/fastdfs/ebidding/data/
rsync -a /fastdfs/store_path0/data/ root@201:/fastdfs/store_path0/data/

已经出现“200 有、201 没有”时,应该怎么修

如果问题已经发生,建议按下面顺序处理,而不是直接把 201 全量拉起来硬接业务:

  1. 1 把 200 当成权威源

    既然已经确认 `200` 有、`201` 没有,那受影响 group 的修复方向就应该是 从 200 补到 201,而不是反过来。

  2. 2 先下线 201 上受影响 group 的 storage

    这样可以避免它继续承接下载请求,防止业务命中缺文件时报错。

  3. 3 只对真实文件目录做补数

    仅同步受影响 group 的实际文件目录,不同步 tracker、base_path、binlog、状态文件、配置、日志和 pid。

  4. 4 至少做两轮 rsync

    因为 `200` 往往还在线承接业务,所以第一轮主要补历史数据,第二轮补增量;如果条件允许,再加一轮停写后的最终增量。

  5. 5 确认 NFS 本身稳定

    至少检查挂载是否稳定、可读可写、权限与属主是否正常、是否有 stale file handle、延迟是否可接受。如果底层 NFS 自身不稳,补完数据也可能继续出问题。

  6. 6 恢复 storage 后做实际读验证

    不要只看 monitor 里是否 `ACTIVE`,还应抽查历史缺失文件、近期新写文件,以及业务下载链路是否稳定。

一个容易忽视的风险点

如果 `201` 已经恢复为 `ACTIVE`,而 tracker 会把下载请求分发给它,那么哪怕只有少量文件没补齐,也会表现成线上偶发失败。这类问题最容易被误判成“网络抖动”或“客户端偶现”。

关于 NFS 的额外提醒

FastDFS 跑在 NFS 上不是绝对不行,但必须承认它比跑在本地盘上更敏感。需要重点关注:

  • NFS 延迟是否明显升高
  • 大量小文件目录访问性能是否足够
  • 属性缓存是否会影响文件可见性
  • 短暂网络抖动是否会把问题放大成文件系统异常
  • 挂载参数是否适合这种高频文件访问模式

如果迁移后持续出现偶发下载失败、同步变慢、目录扫描异常或文件偶尔不可见,就不能只盯着 FastDFS 配置,也要反查 NFS 方案本身。

迁移检查清单

  • 迁移前:明确 `base_path`、`store_path`、group 与实例映射,确认哪些目录是文件数据、哪些目录是运行状态。
  • 切换中:第一次复制只当预同步,最终切换前停止对应 storage,执行最终增量同步,不要整目录覆盖 `/fastdfs`。
  • 切换后:先确认 `ACTIVE`,再抽查历史缺失文件和最新写入文件,观察业务是否仍出现 `file not exist`。

总结

  • FastDFS 存储迁移最容易踩的坑,不是命令写错,而是把“运行中的目录复制”误当成“静态一致快照”。
  • “哪些目录能 rsync、哪些目录不能 rsync”必须在迁移前讲清楚,否则恢复后很容易出现副本不一致。
  • 已经出现 `200` 有、`201` 没有时,应先止血、再补数、最后上线,而不是让不完整副本继续接业务流量。