服务器数据迁移怎么做

结论:服务器数据迁移最稳的做法是"先全量同步、再增量追平、最后短暂停写切换"三板斧:文件用 rsync -avz --progress 传,数据库用 mysqldump 管道直传或主从复制,Docker 用数据卷打包搬迁;切换前 24 小时把 DNS TTL 降到 60 秒,切换后用行数与 md5sum 校验一致性,老服务器保留至少 7 天作为回滚退路。

服务器数据迁移几乎是每个站长和运维都躲不掉的一件事:机房到期、云厂商涨价、配置不够用要升配、业务出海要换地域,甚至只是想把物理服务器迁到云上。很多人的第一反应是打个压缩包用 FTP 下载再上传,结果几十 GB 的数据传了十几个小时,传到一半断线重来,网站也因此关停了大半天。真正靠谱的迁移不靠"打包再传",而靠"增量追平"——先把绝大部分数据慢慢地搬过去(这个阶段业务照常运行),最后一刻只同步那一小段增量,把停机时间压缩到分钟级。截至 2026 年,主流做法依然是 rsync + 数据库逻辑备份 + 镜像/卷快照这三件套的组合拳。本文按"迁前准备、文件迁移、数据库迁移、容器迁移、切换与回滚、一致性校验"六个环节给出可直接复制执行的命令。

迁移前需要做好哪些准备?

迁移前必须先做一次完整备份并写清回滚点,这是唯一不可省略的步骤。没有备份的迁移等于赌运气,一旦新环境起不来,你连"回到昨天"的资格都没有。

准备工作可以按下面这个清单逐项打勾执行,任何一项没做完都不要进入正式的同步阶段:

  • 资产盘点:列出全部待迁数据——网站根目录、上传附件目录、数据库实例、Redis 持久化文件、定时任务(crontab)、Nginx/Apache 配置文件、SSL 证书、Docker Compose 文件与数据卷、环境变量文件。
  • 环境对齐:新服务器的操作系统版本、Web 服务版本、数据库版本尽量与原机一致或更高。跨大版本迁移(如 MySQL 5.7 迁 8.4)必须先在测试环境验证兼容性。
  • 连通性打通:确认新机 SSH 端口(默认 22)已从安全组放行,并配置好密钥登录;临时开放用于 rsync daemon 的 873 端口时注意用完即关。
  • 窗口期选择:选择业务低峰期,通常凌晨 1:00—5:00。切换窗口建议预留 2 小时,即使实际只用到 15 分钟。
  • 回滚预案:老服务器不要立即释放,保留 7 天以上;云主机可以先做快照再停机。

先用下面的命令在新旧两台机器上采集一遍环境指纹,确保版本对齐没有问题:

bash
# 在新旧两台机器上分别执行,对比输出
cat /etc/os-release | head -3
nginx -v
mysqld --version
php -v
df -hT /data
du -sh /var/www/html /data/mysql 2>/dev/null
crontab -l > /tmp/cron.bak && wc -l /tmp/cron.bak

常见迁移工具的选择可以参考这张表:

工具适用场景是否支持断点续传传输加密备注
rsync(over SSH)通用文件迁移,几十 GB 到 TB 级支持,--partialSSH 隧道加密首选方案,增量只传变化块
scp单文件、小目录(< 5 GB)不支持SSH 加密简单但不适合大批量
rclone迁到对象存储(OSS/S3/COS)支持可选云端迁移利器,支持 --transfers 并发
tar 管道 + SSH大量小文件、需要保留软链接不支持SSH 加密小文件场景下比 rsync 快
云盘快照 / 自定义镜像整机迁移、同厂商跨地域不适用平台侧加密最省事,但受厂商锁定限制

文件数据怎么用 rsync 迁移?

rsync(Remote Synchronization)通过"滚动校验 + 差量传输"算法,只发送两端不一致的数据块,因此第二轮同步通常只需要几秒到几分钟。首次执行用全量把数据搬过去,业务不停;切换当天再跑一次增量追平即可。

标准的全量同步命令如下,-a 保留权限属主时间戳等属性,-v 输出详情,-z 传输时压缩,--progress 显示进度:

bash
# 在老服务器上执行,把 /var/www/html 推到新服务器的同一路径
rsync -avz --progress -e "ssh -p 22 -o StrictHostKeyChecking=no" \
  /var/www/html/ root@10.0.0.2:/var/www/html/

# 超大目录建议加 --partial 支持断点续传,并放到后台跑
rsync -avzP --partial --bwlimit=20480 \
  /data/uploads/ root@10.0.0.2:/data/uploads/

# 排除缓存与日志目录,避免传输无用数据
rsync -avz --exclude='cache/' --exclude='*.log' --exclude='node_modules/' \
  /data/ root@10.0.0.2:/data/

几个关键参数的含义需要讲清楚,用错会导致数据不一致:

  • 结尾斜杠很重要:/data/ 表示同步目录内的内容,/data 表示连目录本身一起同步到目标目录下,多套一层。
  • --delete:删除目标端多余文件,让两端严格一致。危险选项,第一次同步不要加,切换那次确认无误后再加。
  • --bwlimit=20480:限速 20480 KB/s(约 20 MB/s),避免把生产带宽跑满影响线上业务。
  • -n / --dry-run:模拟运行,只显示将要发生的变化,不真正传输。正式执行前先跑一遍检查排除规则是否正确。

大量小文件(如图片碎片、session 文件)场景下,rsync 的逐文件校验会成为瓶颈,此时改用 tar 管道直传往往更快:

bash
# 在目标端执行拉取,绕开逐块校验,海量小文件时速度优势明显
ssh root@10.0.0.1 "cd /var/www && tar czf - html" | tar xzf - -C /var/www

数据库怎么迁移最安全?

小库(10 GB 以内)用 mysqldump 管道直传,一行命令完成导出、压缩、传输、导入,全程不落盘,既省空间又省时间。大库(50 GB 以上)应当改用主从复制或物理备份工具(如 Percona XtraBackup),因为逻辑导出的导入时间会线性增长到不可接受的窗口长度。

mysqldump 管道直传的标准写法:

bash
# 一步到位:导出 → gzip → ssh → 解压 → 导入,不在本地产生临时文件
mysqldump -uroot -p'YourPass' \
  --single-transaction --routines --triggers --events \
  --default-character-set=utf8mb4 appdb \
| gzip -9 \
| ssh root@10.0.0.2 "gunzip | mysql -uroot -p'NewPass' appdb"

其中 --single-transaction 是 InnoDB 表实现"不锁表热备"的关键:它在一个事务内拿到一致性快照,迁移期间业务仍可读写。注意该参数只对 InnoDB 生效,如果还有 MyISAM 表,则需要 --lock-all-tables,代价是迁移期间整库只读。

对于大库,推荐的做法是先让新库的实例作为老库的从库追平 binlog,切换时停写、等待 Seconds_Behind_Master 归零、再把从库提升为主库:

bash
# 在从库上确认已追平(Seconds_Behind_Master 为 0 或 NULL 且 Slave_IO_Running=Yes)
mysql -uroot -p -e "SHOW REPLICA STATUS\G" | grep -E "Seconds_Behind|Replica_IO_Running|Replica_SQL_Running"

# 追平后提升为新主库(MySQL 8.0+ 写法,8.4 起推荐用 replica 关键字)
mysql -uroot -p -e "STOP REPLICA; RESET REPLICA ALL; SET GLOBAL read_only=OFF;"

# 迁移前后核对关键表行数,必须完全一致
mysql -uroot -p -e "SELECT COUNT(*) FROM appdb.orders;"

Docker 容器与数据卷怎么迁移?

容器本身是无状态的,真正需要迁移的是镜像清单和数据卷(Volume)。正确姿势是"重建容器 + 搬迁卷",而不是把整个 /var/lib/docker 目录粗暴打包——后者带着大量平台相关元数据,换机器后极容易启动失败。

迁移步骤如下:

  1. 在原机导出 Compose 文件与镜像清单:docker compose config > docker-compose.yml,docker images --format '{{.Repository}}:{{.Tag}}' > images.txt。
  2. 打包命名卷:启动一个临时容器挂载该卷,用 tar 打成归档包。
  3. 传到新机后,用同样的方式把归档解开到同名新卷中。
bash
# 备份名为 app_data 的数据卷到当前目录
docker run --rm -v app_data:/source -v "$PWD":/backup \
  alpine tar czf /backup/app_data.tar.gz -C /source .

# 传输到新服务器
rsync -avzP app_data.tar.gz root@10.0.0.2:/root/

# 在新机上恢复:先建同名卷,再解压灌入
docker volume create app_data
docker run --rm -v app_data:/target -v /root:/backup \
  alpine sh -c "cd /target && tar xzf /backup/app_data.tar.gz"

# 拉取原镜像清单并重建容器
docker compose up -d
docker compose ps

切换、DNS 预热与回滚怎么安排?

DNS 切换能否平滑,取决于你在切换前有没有把 TTL(Time To Live)提前调小。TTL 决定各级递归 DNS 缓存记录的时间,如果原来是 3600 秒,你把解析改到新 IP 后,最坏情况要等 1 小时全网才生效。

标准的 DNS 预热时间线:

  • T-48 小时:把目标域名的 A 记录 TTL 从默认的 3600 秒改为 600 秒。
  • T-24 小时:再把 TTL 降到 60 秒,让全网缓存快速过期。
  • T-0 切换窗口:rsync 最后一次增量同步 → 数据库停写追平 → 修改 A 记录指向新 IP → 观察新机访问日志。
  • T+30 分钟:确认新机流量已承接、无异常报错后,逐步恢复 TTL 到 600 秒。
  • T+7 天:确认一切稳定,再释放老服务器。

切果还想更稳妥,可以做灰度:按地域或按用户比例逐步切流。云厂商的云解析产品支持按线路/权重返回不同 IP,先在某个省或某 5% 的流量上验证,再全量放开。

回滚的前提是"老机器还活着"。只要在释放老服务器之前发现问题,回滚就是把 A 记录改回老 IP,加上一次反向的 rsync 增量(把灰度期新增的数据补回来)即可,整个过程通常 10 分钟内可完成。因此务必写清一句话回滚指令并贴在运维群里:

bash
# 回滚:把灰度期间新机产生的新数据反向同步回老机
rsync -avz --progress root@10.0.0.2:/var/www/uploads/ /var/www/uploads/

迁移后如何校验数据一致性?

校验是迁移的最后一公里,必须由脚本给出客观结果,不能靠"看着差不多"。文件层建议用 md5sum 或 sha256sum 对整个目录生成校验清单比对;数据库层比对关键表的行数与最新记录的 ID/时间戳。

bash
# 两端分别生成校验清单,然后 diff
cd /var/www/html && find . -type f -exec md5sum {} \; | sort -k2 > /tmp/checksum_new.txt
scp root@10.0.0.1:/tmp/checksum_old.txt /tmp/
diff /tmp/checksum_old.txt /tmp/checksum_new.txt && echo "一致性校验通过"

# 数据库行数核对
mysql -uroot -p -e "SELECT TABLE_NAME, TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_SCHEMA='appdb' ORDER BY TABLE_NAME;"

另外别忘了这几项人工 checklist:Nginx 配置已同步且 nginx -t 通过、SSL 证书已迁移且未过期、crontab 定时任务已重建且在新机跑过一次、防火墙与安全组规则已复刻、监控告警的 Agent 已安装并有数据上报。

常见误区 / 排错提示

  • 误区一:直接打包整个系统盘当迁移方案。 /etc、/var/lib 里塞满了与硬件、内核、云平台相关的配置,整盘克隆到不同规格的新机极易卡在启动阶段。正确做法是只迁业务数据与配置,服务用版本化的方式重装。
  • 误区二:忘记同步文件属主和权限。 rsync 不加 -a(或 -p -o -g -t)时,目标端文件会变成 root:root 且权限重置,导致 PHP-FPM 或 Nginx 工作进程无权读写。同步后务必执行 chown -R www:www /var/www/html。
  • 误区三:忽略 SELinux 上下文。 在 CentOS/RHEL 系的新机上,即使权限正确,SELinux context 不对也会报 403。用 restorecon -Rv /var/www/html 修复,或临时 setenforce 0 验证是否为此原因。
  • 误区四:迁移完才发现字符编码不一致。 老库是 utf8(MySQL 的伪 utf8,即 3 字节 utf8mb3)而新库建成了 utf8mb4,emoji 与生僻字会报错。建库时统一用 utf8mb4 并指定排序规则 utf8mb4_0900_ai_ci(MySQL 8.x)。
  • 排错:rsync 报 "Permission denied (publickey)"。 说明新机未信任老机的公钥,先在老机 ssh-copy-id root@10.0.0.2,或临时用 -i /path/to/key.pem 指定私钥。
  • 排错:rsync 报 "No space left on device"。 新机磁盘规划不足。用 df -h 确认,并用 du -sh 重新估算源数据量;云盘支持在线扩容时先扩盘再同步。