前段时间我打算把主站迁到一台新的 VPS 上。旧机器用了两年多,配置有点跟不上了,而且同价位的新机器性能更好一些。迁移前最头疼的就是数据怎么搬:网站文件、用户上传的图片、数据库备份,零零散散加起来有 100G 左右,而且文件数量特别多,小文件占了大头。

如果用 scp 或者 sftp 慢慢拖,按照我这破线路的速度,估计得传好几天,中间断一次还得从头来。所以这次迁移我用的工具是 rsync,整个过程比预想中顺利很多。

为什么选 rsync

rsync 有几个特点特别符合这次迁移的需求:

  1. 增量同步:只传输发生变化的部分,已经传过的文件不会再传。
  2. 断点续传:大文件传到一半断了,下次会从断点继续,不用重来。
  3. 保留权限:可以保留文件的权限、时间戳、所有者这些信息,对 Web 目录特别重要。
  4. 压缩传输-z 参数可以在传输过程中压缩,适合小文件多的场景。

迁移前的准备

迁移之前我先列了一个清单:

  1. 在新服务器上装好同样的环境:Nginx、PHP、MySQL、Node.js 这些。
  2. 确认新服务器的磁盘空间足够,留有一定余量。
  3. 把旧服务器的 SSH 密钥加到新服务器,方便 rsync 直连。
  4. 通知用户或者设置维护页面(如果有的话)。

数据迁移我分了两步走:先迁静态文件,再迁数据库。因为数据库在迁移过程中可能还在变化,所以放在最后一步。

第一次全量同步

第一次同步把旧服务器上的所有数据拉到新服务器。我直接在新服务器上执行拉取命令:

1rsync -avz --progress \
2  -e "ssh -p 22" \
3  root@旧服务器IP:/var/www/myblog/ /var/www/myblog/

参数解释:

  • -a:归档模式,保留权限、符号链接、时间戳等,相当于 -rlptgoD
  • -v:显示详细进度,方便看传输了什么。
  • -z:传输时压缩,适合文本和小文件。
  • --progress:显示每个文件的进度。
  • -e "ssh -p 22":指定 SSH 端口,如果改了端口这里要对应修改。

第一次全量同步花了我大概三四个小时。主要是小文件太多,rsync 要逐个比对,带宽反而没用满。

遇到的第一个坑:大量小文件导致内存不足

同步到一半的时候,连接突然断开,报错 connection unexpectedly closed

排查了一下,发现是因为我有一个目录里存了几百万张缩略图,每张只有几 KB。rsync 在构建文件列表时要把所有文件信息加载到内存,而那台旧机器只有 512M 内存,直接被 OOM 杀了。

解决办法是分目录同步。我写了一个简单的脚本,把大目录拆成多个小批次:

 1#!/bin/bash
 2
 3SRC="root@旧服务器IP:/var/www/myblog/uploads/"
 4DST="/var/www/myblog/uploads/"
 5
 6# 按年份子目录分别同步
 7for dir in 2022 2023 2024; do
 8    echo "正在同步 $dir ..."
 9    rsync -avz --delete -e "ssh -p 22" "$SRC$dir/" "$DST$dir/"
10done

这样每次同步的文件列表就小多了,不会再把内存吃光。

第二次增量同步

全量同步完成后,旧服务器上的数据可能又有更新了。在切换域名解析之前,我再跑一次增量同步:

1rsync -avz --delete \
2  -e "ssh -p 22" \
3  root@旧服务器IP:/var/www/myblog/ /var/www/myblog/

这次加上 --delete 参数,保证两边完全一致。如果新服务器上多出了旧服务器没有的文件,会被删除。

因为大部分文件已经在第一次同步过了,第二次只花了十几分钟。

数据库迁移

静态文件同步完之后,开始迁数据库。我先在旧服务器上做一个完整的 dump:

1mysqldump -u root -p --all-databases --single-transaction > /tmp/all_databases.sql

--single-transaction 参数可以在不锁表的情况下导出 InnoDB 表,对线上服务影响比较小。

然后把 dump 文件传到新服务器:

1rsync -avz --progress -e "ssh -p 22" root@旧服务器IP:/tmp/all_databases.sql /tmp/

在新服务器上导入:

1mysql -u root -p < /tmp/all_databases.sql

导入完成后,我检查了几个关键表的数据量和最近更新时间,确认没有遗漏。

切换前的最终同步

真正切换流量之前,我还做了一次最终同步。这次要尽量减少停机时间。

步骤大概是这样的:

  1. 在旧服务器上暂停写入操作,比如暂时关闭用户上传功能,或者把应用设为只读模式。
  2. 跑最后一次 rsync 增量同步。
  3. 跑最后一次数据库 dump 和导入。
  4. 更新域名解析到新服务器 IP。
  5. 等新服务器开始承接流量后,再打开写入功能。

整个切换过程实际停机时间大概 10 分钟左右,主要花在数据库导入上。

验证和收尾

切换之后,我重点检查了这些东西:

  1. 网站首页、文章页、搜索功能是否正常。
  2. 图片、CSS、JS 等静态资源是否能正常加载。
  3. 数据库读写是否正常。
  4. Nginx 日志有没有报错。
  5. SSL 证书是否生效。

全部确认没问题后,我在旧服务器上保留了一个星期的数据作为保险,然后才清理掉。

总结

这次迁移让我对 rsync 的理解更深了一层。几个关键点:

  1. 全量 + 增量 + 最终同步 是减少停机时间的标准做法。
  2. 大量小文件要分目录同步,避免内存问题。
  3. --delete 要谨慎使用,确保目标目录是对的。
  4. 数据库和静态文件分开迁移,数据库放在最后。
  5. 迁移前备份,迁移后验证,不要嫌麻烦。

rsync 真的算得上运维人员的神器之一。只要网络通,它就能把数据稳稳当当地搬过去。下次如果你也要迁移服务器,不妨试试这个方案。