
更换云服务器时,真正困难的通常不是把文件复制过去,而是如何处理迁移期间仍在发生的写入:用户上传了新图片、订单状态改变、数据库又产生了增量数据,而 DNS 缓存中的旧 IP 还没有完全失效。如果只做一次全量拷贝再改解析,很容易出现文件缺失、数据库回退、登录状态异常或部分用户仍访问旧站。
本文给出一套可执行的"双阶段迁移"方案:先搭建新实例并完成全量同步,再持续追平文件与数据库增量;切换前短暂冻结写入,确认数据一致后修改流量入口,同时保留旧服务器作为可回滚节点。目标是把停机窗口压缩到可控范围,而不是不负责任地承诺绝对零停机。
一、适用场景
这套方法适合:
- 更换云厂商、地域、账号或网络线路;
- 从低配置实例升级到新的云服务器,但不能直接原地升配;
- 从单机迁移到应用与数据库分离架构;
- 网站、API、WordPress、Java/PHP/Node.js 服务需要缩短停机;
- 需要迁移 Nginx、应用文件、用户上传目录和 MySQL 数据;
- 续费前希望比较新老方案,并保留快速回滚能力。
如果业务包含支付、库存、消息队列、对象存储、定时任务或多主写入,迁移方案还要覆盖幂等、顺序、一致性和第三方回调,不能只照搬文件加数据库的流程。
二、迁移前先盘点,不要先买机器再猜配置
先记录旧服务器真实资源,而不是只记"4 核 8G":
bash
lscpu
free -h
lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINTS
df -hT
df -i
ip -br addr
ss -lntup
再记录高峰期 CPU、内存、磁盘与网络:
bash
vmstat 1 60
iostat -xz 1 60
sar -n DEV 1 60
需要盘点的对象至少包括:
- 操作系统、内核、时区、语言环境;
- Nginx/Apache、运行时、依赖库和服务版本;
- 网站代码、配置、证书、用户上传和持久化目录;
- MySQL 版本、字符集、排序规则、存储引擎和数据量;
- Redis、消息队列、定时任务、systemd 服务和容器卷;
- 域名解析、固定 IP 白名单、第三方回调和防火墙规则;
- 云盘容量、IOPS、吞吐、快照和备份策略。
配置判断
新实例不要机械复制旧规格。如果旧服务器 CPU 长期空闲但磁盘 await 很高,应优先核对云盘性能;如果应用经常 OOM,应检查内存峰值与容器限制;如果出口带宽持续触顶,应同时评估带宽和静态资源分发。迁移正是重新匹配计算、内存、磁盘、带宽和地域的机会。
三、目标架构:全量同步、增量追平、短暂冻结、可回滚

推荐把迁移拆成四个状态:
- 准备:新服务器只对运维人员开放,完成环境和安全配置;
- 全量:复制大部分静态文件与数据库基线,旧站继续服务;
- 增量:持续同步新增文件和数据库变更,缩小差距;
- 切换:短暂冻结旧站写入,做最后同步和校验,再切流量。
切换后不要立刻销毁旧服务器。先停止旧站写入和定时任务,将其保留为只读回滚点,观察一个完整业务高峰后再决定释放。
四、第一步:新服务器先搭好安全基线
创建新实例后,先完成:
- 使用密钥或受控方式登录,禁止弱密码;
- 安全组只开放当前需要的端口;
- 数据库端口不直接暴露公网;
- 安装系统更新、时间同步和监控;
- 创建与旧环境一致的用户、目录和权限;
- 部署 Nginx、应用运行时与数据库,但暂不承接公网流量。
检查时间与网络:
bash
timedatectl
chronyc tracking 2>/dev/null || true
curl -4 ifconfig.me
如果两台服务器需要通过公网同步,应限制源 IP、使用 SSH 密钥并在迁移后撤销临时规则。更稳妥的做法是使用受控专线、VPN、对等连接或云厂商提供的迁移通道,但要核对跨地域和跨厂商可用性。
五、第二步:用 rsync 做文件全量与增量同步
1. 先做演练
在新服务器执行 dry-run:
bash
rsync -aHAXvn --numeric-ids --delete-delay \
-e 'ssh -p 22' root@OLD_IP:/srv/app/ /srv/app/
参数含义:
-a保留常用属性并递归同步;-HAX尝试保留硬链接、ACL 和扩展属性;--numeric-ids按数字 UID/GID 处理所有者;-n只演练,不实际修改;--delete-delay延后删除目标端多余文件。
--delete-delay 有破坏性,只有确认目标目录是本次迁移副本且方向无误时才使用。首次执行建议先去掉删除参数并保存日志。
2. 正式全量同步
bash
rsync -aHAXv --numeric-ids --partial --info=progress2 \
--exclude='cache/' --exclude='tmp/' \
-e 'ssh -p 22' root@OLD_IP:/srv/app/ /srv/app/
迁移 /etc 时不要整目录覆盖新系统。应只同步明确需要的站点配置、证书和服务单元,并逐项审查 IP、路径、用户与版本差异。
3. 增量同步
全量完成后定时再次运行相同的 rsync,每次只传变化部分。用户上传目录应重点追踪;缓存、临时文件和可重新构建的依赖可以排除,减少切换窗口。
同步后检查:
bash
du -sh /srv/app
find /srv/app -type f | wc -l
rsync -aHAXvn --numeric-ids -e 'ssh -p 22' \
root@OLD_IP:/srv/app/ /srv/app/
最后一次 dry-run 应只剩可解释的运行时变化。
六、第三步:MySQL 用可验证的方式追平增量
数据库迁移不能把正在运行的 MySQL 数据目录直接 rsync 到另一台机器。InnoDB 文件、redo log 和运行时状态需要一致性工具处理。
方案 A:停写窗口可接受
业务量较小时,可以先用一致性逻辑备份建立基线,切换时短暂停写并导出最后增量或重新导入:
bash
mysqldump --single-transaction --routines --triggers --events \
--hex-blob --set-gtid-purged=OFF --all-databases \
| gzip > /backup/mysql-full.sql.gz
--single-transaction 主要保证 InnoDB 一致性;如果存在 MyISAM 等非事务表,需要单独设计锁与一致性方案。导出前检查磁盘空间,导入后检查用户、权限、事件和字符集。
方案 B:用复制或 binlog 缩短停写
数据量大、停机窗口短时,可让新 MySQL 先恢复全量基线,再从对应的 binlog 位点或 GTID 持续追赶。基本链路是:
text
旧 MySQL 持续写入 -> binlog -> 新 MySQL 复制追平 -> 切换前停止写入 -> 等待延迟归零
实施前必须确认:
- 源端已正确启用 binlog;
- 新旧 MySQL 版本与复制方向受支持;
server_id、GTID、字符集和时区配置一致;- 复制账户仅授予必要权限并限制来源;
- 没有在新库提前写入造成数据分叉;
- 可以监控复制错误和延迟。
MySQL 8 可使用:
sql
SHOW MASTER STATUS;
SHOW REPLICA STATUS\G
不同版本命令可能仍使用 SHOW SLAVE STATUS。不要只看 Seconds_Behind_Source;还要检查 IO/SQL 线程状态、Last_IO_Error、Last_SQL_Error,并在最终停写后确认位点或 GTID 已追平。
七、第四步:提前降低 DNS TTL,但别把 DNS 当成秒级开关
在计划切换前一个原 TTL 周期,将需要修改的记录 TTL 调低,例如从较长时间降低到几分钟级。具体值应根据 DNS 服务能力、查询量和业务风险决定,不虚构"所有用户几分钟内必然生效"。
检查解析:
bash
dig example.com A
dig @1.1.1.1 example.com A
dig @8.8.8.8 example.com A
即使权威 DNS 已更新,递归缓存、操作系统、浏览器和本地网络仍可能保留旧地址。因此旧服务器必须继续响应一段时间,不能改完解析就立即关机。
如果入口使用负载均衡、EIP、反向代理或全局流量调度,可降低直接切换主机 IP 的复杂度,但仍需验证健康检查、会话、证书和源站白名单。
八、第五步:切换前短暂冻结写入
选择低峰窗口,按顺序执行:
- 暂停旧服务器上的定时任务、队列消费者和批处理;
- 将旧站切为维护或只读模式,阻止新写入;
- 执行最后一次文件增量同步;
- 等待数据库复制追平并确认无错误;
- 在新服务器本地验证应用、数据库和静态文件;
- 修改 DNS、负载均衡或流量入口;
- 继续保持旧服务器只读,观察真实请求。
常见暂停方式需按实际服务调整:
bash
sudo systemctl stop app-worker.service
sudo systemctl list-timers --all
sudo crontab -l
不要粗暴停止整台机器,否则失去最后校验和快速回滚能力。
九、切换前验证:绕过 DNS 直接访问新服务器
使用 curl --resolve 将域名临时指向新 IP,不修改本机 hosts:
bash
curl -vk --resolve example.com:443:NEW_IP https://example.com/
检查:
- TLS 证书和 SNI 是否正确;
- 首页、登录、上传、下单、回调等关键路径;
- 静态资源与跨域配置;
- 新服务器访问数据库、缓存和对象存储;
- Nginx、应用、数据库日志是否有错误;
- 防火墙、安全组、IP 白名单与第三方回调;
- 新实例 CPU、内存、云盘和公网带宽是否满足预期。
还可以比较关键目录校验值。对大量文件,不要在高峰期无差别计算全盘哈希;优先检查配置、发布包和关键上传目录:
bash
find /srv/app/releases/current -type f -print0 \
| sort -z | xargs -0 sha256sum > /tmp/app.sha256
十、回滚条件要在切换前写清楚

建议预先定义触发回滚的指标,例如:
- 关键接口错误率超过业务阈值;
- 登录、支付、下单或上传不可用;
- 数据库复制或数据校验异常;
- 新服务器资源持续饱和;
- 延迟明显高于旧环境且无法快速修复。
回滚并不只是把 DNS 改回去。如果新环境已经接受写入,直接切回旧库会丢失新数据。更安全的选择是:在确认稳定前,让单一环境保持写入权;如果新环境已经写入,则必须设计反向同步或业务补偿,不能盲目回切。
十一、迁移后的验证方法
至少观察一个完整业务高峰,并核对:
- DNS 在主要地区解析到预期入口;
- 新服务器请求量逐步上升,旧服务器请求量逐步下降;
- 关键接口 P50/P95/P99、错误率和吞吐;
- CPU、内存、Swap、云盘延迟、IOPS 和带宽;
- MySQL 连接数、慢查询、锁等待和磁盘增长;
- 用户上传、定时任务、消息队列和第三方回调;
- 证书自动续期、监控、告警、备份和恢复演练。
查看仍访问旧服务器的请求:
bash
sudo tail -f /var/log/nginx/access.log
确认旧流量降低到可接受范围后,再停止旧应用,但保留快照或备份到约定期限。删除旧资源前要再次确认数据、账单、快照和回滚期限。
十二、常见误区
误区 1:rsync 一次就完成迁移
错误。全量同步期间旧站仍会产生增量,必须再次追平并设置最终停写点。
误区 2:直接复制正在运行的 MySQL 数据目录
风险很高。应使用一致性备份、物理备份工具或受支持的复制方案。
误区 3:DNS TTL 调低后所有用户都会立刻切换
错误。多级缓存和客户端行为无法完全控制,旧入口应继续服务。
误区 4:新服务器配置越高,迁移就越安全
迁移风险主要来自数据一致性、入口切换、依赖和回滚。高配置不能替代流程。
误区 5:切换成功就立即删除旧服务器
应先覆盖完整高峰,验证监控、备份、回调和残余流量,再按计划释放。
十三、购买新云服务器时该怎么选
迁移前用旧环境的高峰数据决定新配置:
- CPU:看运行队列、P95 使用率和应用是否可并行;
- 内存:看可用内存、Swap、数据库缓存和容器峰值;
- 云盘 :看数据量、增长率、
await、IOPS、吞吐和快照需求; - 带宽:看公网峰值、文件大小、并发和是否配合 CDN;
- 地域:靠近主要用户和依赖,并满足合规与容灾要求;
- 高可用:根据允许的 RPO、RTO 选择备份、主从、多可用区或灾备。
如果只是规格不足,可以选择更合适的实例与云盘;如果单机同时承载应用、数据库、缓存和备份导致互相争抢,迁移时应考虑拆分;如果业务不能接受长时间停机,则应提前准备复制、负载均衡和回滚,而不是在切换当天临时补救。
云服务器迁移本质上是一项风险控制工程。先用数据确定新配置,再用全量加增量缩短停机,用明确的停写点保证一致性,用旧节点和备份保留回滚能力,才能让一次购买或迁移真正换来性能、稳定性和后续扩展空间。