FastDFS 存储迁移到 NFS 的注意事项与回滚建议
这篇文章来自一次真实的迁移事故:原本双节点正常运行的 FastDFS,在一台节点把本地盘切到 NFS 后,出现了 200 有文件、201 没有文件 的副本不一致现象。真正需要解决的,不只是“文件怎么补回来”,而是如何避免把不完整副本重新暴露给业务。
问题现象
`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
拷贝时源目录仍在变化
如果在 `201` 仍然在线提供服务时执行了
cp或rsync,那么复制期间/fastdfs下仍可能发生新文件写入、同步线程补数、元数据变化。复制到 NFS 的数据,本质上只是某个时刻的近似快照,而不是严格冻结后的最终数据。 -
2
FastDFS 本身是异步复制
FastDFS 同组 storage 一般是“先写源节点,再由副本异步同步”。所以“上传成功”并不等于另一台副本已经完整落盘。只要迁移正好发生在同步窗口内,就有机会留下尾差。
-
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
迁移前先划清目录边界
必须明确每个 storage 实例的
base_path、store_path_count、每个store_path实际指向哪里,以及每个 group 对应哪个端口与实例。不要对整个/fastdfs一把梭。 -
2
第一次复制只当预同步
第一轮 rsync 只能解决大部分历史数据搬运,不能被视为最终一致结果。它的作用是先降低总数据量,不是替代最终切换。
-
3
最终切换前停对应 storage
最稳妥的流程应是:停止目标 group 的 storage → 执行最终增量同步 → 切换挂载 → 再恢复 storage。这样可以把迁移窗口压到最小。
-
4
高写入 group 最好安排短暂停写
如果业务写入很频繁,即使做了两轮 rsync,也可能在最后恢复前留下尾差。最稳妥的做法,是在最终增量同步前短暂停写几分钟。
-
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
把 200 当成权威源
既然已经确认 `200` 有、`201` 没有,那受影响 group 的修复方向就应该是 从 200 补到 201,而不是反过来。
-
2
先下线 201 上受影响 group 的 storage
这样可以避免它继续承接下载请求,防止业务命中缺文件时报错。
-
3
只对真实文件目录做补数
仅同步受影响 group 的实际文件目录,不同步 tracker、base_path、binlog、状态文件、配置、日志和 pid。
-
4
至少做两轮 rsync
因为 `200` 往往还在线承接业务,所以第一轮主要补历史数据,第二轮补增量;如果条件允许,再加一轮停写后的最终增量。
-
5
确认 NFS 本身稳定
至少检查挂载是否稳定、可读可写、权限与属主是否正常、是否有
stale file handle、延迟是否可接受。如果底层 NFS 自身不稳,补完数据也可能继续出问题。 -
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` 没有时,应先止血、再补数、最后上线,而不是让不完整副本继续接业务流量。