数据库跨服务器迁移完整方案:MySQL零停机迁移、主从同步与数据验证实战

数据库迁移是最风险的运维操作之一——一旦出错可能导致数据丢失或业务长时间中断。本文提供两套方案:适合小型数据库的快速迁移方案,以及适合大型生产数据库的零停机迁移方案。

一、迁移前评估

# 评估数据库大小
mysql -u root -p -e "
SELECT table_schema AS '数据库',
       ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS '大小(MB)'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;"

# 评估表数量和最大表
mysql -u root -p -e "
SELECT table_schema, table_name,
       ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.tables
ORDER BY size_mb DESC LIMIT 20;"

# 查看当前连接数和活跃事务
mysql -u root -p -e "SHOW PROCESSLIST;"
mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | grep -A5 "TRANSACTIONS"

二、方案A:快速迁移(停机时间约5~30分钟)

适用于数据库大小在10GB以内,业务可以接受短暂停机的场景。

<code">#!/bin/bash
# /usr/local/bin/db-migrate-quick.sh

OLD_DB_HOST="旧服务器IP"
NEW_DB_HOST="新服务器IP"
DB_USER="root"
DB_PASS="数据库密码"
BACKUP_FILE="/tmp/full_backup_$(date +%Y%m%d_%H%M%S).sql.gz"

echo "=== 步骤1:停止写入(通知应用层暂停) ==="
# 在应用服务器上:停止Nginx/PHP-FPM 或 切换维护页
# systemctl stop nginx

echo "=== 步骤2:在旧服务器上导出数据库 ==="
mysqldump -h "$OLD_DB_HOST" -u "$DB_USER" -p"$DB_PASS" \
    --all-databases \
    --single-transaction \
    --master-data=2 \
    --flush-logs \
    --routines \
    --triggers \
    --events \
    | gzip > "$BACKUP_FILE"

echo "备份完成,大小:$(du -sh $BACKUP_FILE)"

echo "=== 步骤3:传输到新服务器 ==="
scp "$BACKUP_FILE" root@"$NEW_DB_HOST":/tmp/

echo "=== 步骤4:在新服务器上恢复 ==="
ssh root@"$NEW_DB_HOST" "
    gunzip < $BACKUP_FILE | mysql -u $DB_USER -p'$DB_PASS'
    echo '恢复完成'
"

echo "=== 步骤5:更新应用配置,指向新数据库 ==="
# 修改 wp-config.php 或应用配置文件的 DB_HOST

echo "=== 步骤6:恢复服务 ==="
# systemctl start nginx

三、方案B:零停机迁移(基于主从复制)

适用于大型数据库或无法接受停机的生产环境。核心思路:先建立主从同步,待新服务器数据完全同步后,再切换应用连接。

阶段1:配置旧服务器为主节点

<code"># 在旧服务器(主节点)的MySQL配置中添加:
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_do_db = 需要同步的数据库名  # 不填则同步所有库
<code">-- 创建专用复制账号
CREATE USER 'replica'@'新服务器IP' IDENTIFIED BY '复制账号密码';
GRANT REPLICATION SLAVE ON *.* TO 'replica'@'新服务器IP';
FLUSH PRIVILEGES;

-- 获取当前二进制日志位置(用于配置从节点)
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
-- 记录 File 和 Position 的值,例如:
-- File: mysql-bin.000123
-- Position: 456789
UNLOCK TABLES;

阶段2:初始化新服务器并建立同步

<code">-- 在新服务器上导入初始数据后,配置从节点
CHANGE MASTER TO
    MASTER_HOST='旧服务器IP',
    MASTER_USER='replica',
    MASTER_PASSWORD='复制账号密码',
    MASTER_LOG_FILE='mysql-bin.000123',  -- 上面记录的File
    MASTER_LOG_POS=456789;              -- 上面记录的Position

START SLAVE;

-- 验证同步状态(两个Yes表示正常)
SHOW SLAVE STATUS\G

-- 关键字段:
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0  ← 0表示已追上主节点,无延迟

阶段3:等待完全同步后切换

<code">-- 监控同步延迟,等待降为0
-- 在新服务器上持续查看:
SHOW SLAVE STATUS\G | grep "Seconds_Behind_Master"

-- 切换时刻(Seconds_Behind_Master = 0时执行):
-- 1. 在旧服务器暂停写入(停止应用或锁表)
FLUSH TABLES WITH READ LOCK;

-- 2. 等待新服务器同步完毕
-- 新服务器:SHOW SLAVE STATUS\G 确认 Seconds_Behind_Master = 0

-- 3. 停止从节点复制
-- 在新服务器执行:
STOP SLAVE;
RESET SLAVE ALL;  -- 清除复制配置,新服务器变为独立主节点

-- 4. 更新应用配置,将DB_HOST改为新服务器IP

-- 5. 解锁旧服务器
UNLOCK TABLES;

四、数据一致性验证

<code">#!/bin/bash
# 迁移后验证数据一致性

OLD="旧服务器IP"
NEW="新服务器IP"
USER="root"
PASS="密码"

echo "=== 对比各数据库的表数量 ==="
mysql -h "$OLD" -u "$USER" -p"$PASS" -e "
SELECT table_schema, COUNT(*) as tables
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','performance_schema','sys','mysql')
GROUP BY table_schema;" > /tmp/old_tables.txt

mysql -h "$NEW" -u "$USER" -p"$PASS" -e "
SELECT table_schema, COUNT(*) as tables
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','performance_schema','sys','mysql')
GROUP BY table_schema;" > /tmp/new_tables.txt

diff /tmp/old_tables.txt /tmp/new_tables.txt && echo "✅ 表数量一致" || echo "❌ 表数量不一致"

echo "=== 对比关键表的行数 ==="
for DB in wordpress shop; do
    for TABLE in posts users options; do
        OLD_COUNT=$(mysql -h "$OLD" -u "$USER" -p"$PASS" -se "SELECT COUNT(*) FROM $DB.$TABLE 2>/dev/null")
        NEW_COUNT=$(mysql -h "$NEW" -u "$USER" -p"$PASS" -se "SELECT COUNT(*) FROM $DB.$TABLE 2>/dev/null")
        if [ "$OLD_COUNT" = "$NEW_COUNT" ]; then
            echo "✅ $DB.$TABLE: $OLD_COUNT 行"
        else
            echo "❌ $DB.$TABLE: 旧=$OLD_COUNT 新=$NEW_COUNT 不一致!"
        fi
    done
done

五、应用层配置更新

<code"># WordPress配置更新
sed -i "s/define('DB_HOST', '旧服务器IP')/define('DB_HOST', '新服务器IP')/" /var/www/html/wp-config.php

# 验证连接
wp db check --allow-root

# 清除WordPress缓存
wp cache flush --allow-root
wp rewrite flush --allow-root

六、迁移后监控

<code"># 迁移后24小时内持续监控:

# 1. 错误日志
tail -f /var/log/mysql/error.log

# 2. 慢查询(确认索引在新服务器上同样生效)
tail -f /var/log/mysql/slow.log

# 3. 应用错误(PHP日志)
tail -f /var/log/php/error.log

# 4. 监控新服务器负载
htop
watch -n 5 'mysql -u root -p密码 -e "SHOW PROCESSLIST;"'

七、总结

小型数据库(10GB以内)选方案A,停机迁移简单可靠;大型生产数据库选方案B,基于主从复制实现零停机切换。迁移后必须进行数据一致性验证,保留旧服务器运行24~48小时作为应急回退选项。IDC.Net的香港VPS同账号多台服务器支持内网互通,主从同步流量走内网,速度快且不占用公网带宽配额。

THE END