MariaDB 中文社区

跨云迁移实战

AWS ↔ 阿里云 ↔ SkySQL ↔ 自建——双写切流、DTS、Debezium 三种方案对比

何时需要跨云迁移

  • 业务出海 / 回国
  • 成本优化
  • 厂商锁定脱钩
  • 合规重定区

三种迁移方案

方案 1:离线全量 + 短停机

+---------+    dump     +-----------+
| Source  | --------->  |   S3      |
+---------+             +-----+-----+
                              |
                              v
                        +-----------+
                        |  Target   |
                        +-----------+
  • 优点:简单可靠、无双写复杂度
  • 缺点:停机几小时到几天,看库大小
  • 适合:可停机的小库

方案 2:DTS / 第三方托管

工具源 → 目标
AWS DMSAWS Aurora/RDS → 任意 MySQL 兼容
阿里云 DTS阿里 RDS → 任意目标
腾讯 DTS类似
Striim任意 → 任意
Fivetran偏 ETL,不是 0 停机
  • 优点:托管、CDC 自动
  • 缺点:贵、特定路径才支持

方案 3:自建 Debezium + 应用双写

最灵活但要自己运维。下面详细讲。

双写切流完整流程

1. 准备 target 端

# 在目标环境拉起 MariaDB(自建或托管 RDS)
# 确保版本、字符集、collation 与 source 一致

mariadb -h target -e "SHOW VARIABLES LIKE '%char%'"
mariadb -h target -e "SHOW VARIABLES LIKE '%coll%'"

2. 初始全量同步

从 source 只读副本拿 dump,避免影响生产:

mysqldump -h source-readonly \
  --single-transaction --routines --triggers --events \
  --set-gtid-purged=OFF \
  --master-data=2 \
  --all-databases > dump.sql

# 替换不兼容的 collation(MySQL 8 → MariaDB)
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' dump.sql
sed -i 's/utf8mb4_0900_as_cs/utf8mb4_unicode_ci/g' dump.sql

# 导入 target
mariadb -h target -uroot -p < dump.sql

# 记录 binlog 起点
head -100 dump.sql | grep CHANGE

3. 启动 Debezium 持续同步

# debezium-source.json
{
  "name": "source-binlog",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "source-readonly",
    "database.port": "3306",
    "database.user": "debezium",
    "database.password": "...",
    "database.server.id": "184054",
    "database.server.name": "source",
    "database.include.list": "app",
    "table.include.list": "app.*",
    "database.history.kafka.bootstrap.servers": "kafka:9092",
    "database.history.kafka.topic": "schema-history",
    "snapshot.mode": "schema_only",
    "include.schema.changes": "true"
  }
}

下游 sink:

# jdbc-sink.json
{
  "name": "target-sink",
  "config": {
    "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector",
    "connection.url": "jdbc:mariadb://target:3306/app",
    "connection.user": "sink",
    "connection.password": "...",
    "topics.regex": "source\\.app\\..*",
    "insert.mode": "upsert",
    "pk.mode": "record_key",
    "transforms": "unwrap,route",
    "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
    "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
    "transforms.route.regex": "source\\.app\\.(.+)",
    "transforms.route.replacement": "$1"
  }
}

4. 应用层双写

应用代码改成"主写 source,shadow 写 target":

async function write(sql: string, params: any[]) {
  await sourcePool.query(sql, params);                    // 真主
  targetPool.query(sql, params).catch((e) => {            // shadow,不阻塞主
    metrics.shadowWriteFail.inc();
    log.warn('shadow write failed', { e, sql });
  });
}

双写的意义:CDC 可能有 bug 或延迟。双写是兜底。

5. 数据一致性对比

# 用 pt-table-checksum
pt-table-checksum --replicate=app.checksums \
  h=source --recursion-method=hosts

# 或自写:抽样对比
mariadb -h source -e "SELECT id, MD5(CONCAT_WS(',', col1, col2)) FROM users ORDER BY id LIMIT 1000" > s.csv
mariadb -h target -e "SELECT id, MD5(CONCAT_WS(',', col1, col2)) FROM users ORDER BY id LIMIT 1000" > t.csv
diff s.csv t.csv

至少跑 24 小时观察。

6. 切读流量

应用层 feature flag 灰度:

const useTarget = ff.isEnabled('use_target_db', userId);
const pool = useTarget ? targetPool : sourcePool;

5% → 10% → 50% → 100%,每档观察 24 小时。

7. 切写流量

const writeTarget = ff.isEnabled('write_target_only');
if (writeTarget) {
  await targetPool.query(sql, params);
  sourcePool.query(sql, params).catch(/* shadow */);     // 反向 shadow,应急回滚用
} else {
  await sourcePool.query(sql, params);
  targetPool.query(sql, params).catch(/* shadow */);
}

8. 切完后保留至少 2 周

留 source 端只读,应急时能切回。2 周后下线 source。

几种典型路径

AWS RDS → 阿里云 RDS

  • 跨区网络延迟高(150ms+),CDC 延迟会拉到秒级
  • 阿里云 RDS 默认版本旧,需要 source 端先降版本兼容
  • 阿里云 RDS 不给 SUPER 权限,复制起点设置略麻烦

推荐:阿里云 DTS(按量付费,专门优化)

阿里云 → AWS RDS

  • 阿里 RDS 不开 binlog 给外部?要开"日志服务"或申请
  • 字符集兼容(阿里偏旧版本)

推荐:DataGrip / 自建 Debezium,DTS 反向支持差

任意 → SkySQL

SkySQL 提供专门的 ingestion 工具:

skysql ingest start \
  --source-uri=mysql://source-host:3306/app \
  --target-service=skysql-prod

支持 ColumnStore / Xpand 目标,普通 MariaDB 也行。

任意 → 自建 K8s

# 用 mariadb-operator 创 target
kubectl apply -f mariadb-prod.yaml

# 等就绪
kubectl wait --for=condition=Ready mariadb/mariadb-prod --timeout=10m

# 接 Debezium
helm install debezium debezium/debezium-operator

详见 容器化

Aurora → MariaDB

特殊路径,详见 从 Aurora 迁移

回滚预案

任何切流都必须有回滚预案:

  1. 读流量切回:feature flag 一键
  2. 写流量切回:feature flag 一键 + 重放最近 N 分钟 binlog
  3. 完全放弃 target:source 始终有完整数据

"回滚比切流难得多"——如果切流前没演练回滚,等于裸奔。

验证清单

  • 字符集 / collation 完全一致
  • 时区一致(SELECT @@time_zone
  • sql_mode 一致
  • 所有 stored procedure / trigger / event 复制过去
  • 主键自增起点:target 比 source 大 100 万(防冲突)
  • 触发器、外键、CHECK 约束
  • 全部 user 重新 GRANT
  • 全部 view 重建
  • 应用配置切完
  • 监控 dashboard 切完
  • 备份策略迁移到新环境
  • DNS / 域名 / VPC 路由切完
  • 旧环境断网测试,确认无应用调用

时间预估

库大小全量 dump全量恢复CDC 追平总停机(双写)
10 GB10 分钟30 分钟即时1 分钟(切流)
100 GB2 小时6 小时< 5 分钟1 分钟
1 TB12 小时36 小时30 分钟5 分钟
10 TB数天数天数小时视情况

10 TB+ 建议用 mariabackup + xtrabackup 增量 或物理拷贝。

延伸

本页目录