华为云信创迁移实战:MySQL/Oracle到openGauss平滑迁移全指南

举报
行者·全栈架构师 发表于 2026/06/02 23:09:02 2026/06/02
【摘要】 本文记录了作者在实际项目中,将 MySQL 5.7 和 Oracle 11g 两套业务系统迁移到 openGauss 5.0 的全过程。对比了 chameleon 和 DataKit 两种迁移工具的适用场景、性能和踩坑点,总结了 SQL 兼容性处理、数据类型映射、增量同步延迟控制等核心难点。文末附迁移检查清单和性能对比数据,适合正在做信创数据库替换的团队参考。

🚀 华为云信创迁移实战:MySQL/Oracle到openGauss平滑迁移全指南

📝 文章摘要:本文记录了作者在实际项目中,将 MySQL 5.7 和 Oracle 11g 两套业务系统迁移到 openGauss 5.0 的全过程。对比了 chameleon 和 DataKit 两种迁移工具的适用场景、性能和踩坑点,总结了 SQL 兼容性处理、数据类型映射、增量同步延迟控制等核心难点。文末附迁移检查清单和性能对比数据,适合正在做信创数据库替换的团队参考。

⏱ 预计阅读时间:18 分钟

🎯 背景:为什么我们要从 MySQL/Oracle 迁移到 openGauss?

2025 年初,我所在的公司启动了信创改造项目。核心任务是把两个生产系统迁移到国产数据库:

系统 原有数据库 数据量 日均 QPS 迁移目标
订单中心 MySQL 5.7 约 800GB,2 亿行 3,500+ openGauss 5.0
财务系统 Oracle 11g R2 约 1.2TB,3.5 亿行 1,200+ openGauss 5.0

需求很明确:

  • 必须零数据丢失,双跑验证期至少 2 周
  • RTO < 30 分钟,回滚方案要提前验证
  • 性能不能比原来差,尤其是订单中心的写入链路

选 openGauss 而非 GaussDB,主要原因是:我们团队对 PostgreSQL 生态比较熟悉,openGauss 兼容 PostgreSQL 协议,学习成本相对低,而且是开源版本,云上云下都能部署。


🔍 迁移工具选型:chameleon vs DataKit

openGauss 官方推荐两种迁移工具。我们一开始全量用 chameleon 做,踩了不少坑;后来 DataKit 出了图形化版本,增量部分切了过去。

chameleon

# 安装(Python 3.8+ 环境要求)
pip install openGauss-tools-chameleon

# 初始化迁移配置
chameleon set_config_file mysql_to_opengauss.yaml

# 执行全量迁移
chameleon start_migration mysql_to_opengauss.yaml

# 开始持续复制
chameleon start_replica mysql_to_opengauss.yaml

优点:开源、命令行可控、支持断点续传,适合脚本化批量迁移。

缺点:配置项散落在 YAML 里,类型映射表需要手动维护,遇到异常报错信息比较晦涩。

DataKit

# 下载 DataKit
wget https://opengauss.obs.cn-south-1.myhuaweicloud.com/datakit/5.0.0/datakit-5.0.0-linux-x86_64.tar.gz
tar -xzf datakit-5.0.0-linux-x86_64.tar.gz
cd datakit/bin
./datakit --start  # 启动 Web 管理页面,默认端口 8449

优点:图形化配置、自动类型映射、自带数据校验对比、增量同步可视化。

缺点:对于 50GB+ 的大表,首次全量阶段偶尔会 OOM,需要调 JVM 参数。


🏗️ 迁移架构设计

HWG-001-mysql-oracle-to-opengauss-migration_diagram_1.png

我们采用的是 全量 + 增量 + 双跑验证 三阶段策略:

  1. T 日:停止源库写入 → chameleon 执行全量导出导入
  2. T+1 ~ T+14:DataKit 开启增量同步,业务流量同时写入源库和目标库,对比结果
  3. T+15:确认无差异 → 切换读流量 → 再观察 3 天 → 切换写流量

🛠️ 实施步骤与踩坑记录

Step 1:全量迁移 — chameleon

chameleon 的配置主要在 YAML 里定义源端和目标端的连接、表映射、类型转换规则。

# mysql_to_opengauss.yaml(核心部分)
source:
  type: mysql
  host: 192.168.1.100
  port: 3306
  user: migrate_user
  password: migrate_pass
  database: order_db
  tables:
    - orders
    - order_items
    - users
    - products

target:
  type: opengauss
  host: 192.168.2.100
  port: 5432          # openGauss 默认端口
  user: omm
  password: og_pass
  database: order_db

type_mapping:
  mysql.tinyint(1): boolean
  mysql.datetime: timestamp without time zone
  mysql.decimal(10,2): numeric(10,2)

replica:
  mode: log_based
  checkpoint_interval: 30

💥 踩坑 1:tinyint(1) 被当作 boolean

现象:MySQL 中 is_active tinyint(1) 迁移到 openGauss 后变成了 boolean,Java 应用中 getInt() 强转抛出 ClassCastException

排查:翻 chameleon 日志发现自动类型映射把 tinyint(1) 当作 boolean。这是 MySQL JDBC 驱动的历史问题 —— tinyint(1) 默认被映射成 Boolean。

解决:在类型映射配置中显式指定:

type_mapping:
  mysql.tinyint(1): smallint  # 不是 boolean,是 smallint!

跑完后手动验证一遍全表的字段类型:

SELECT column_name, data_type 
FROM information_schema.columns 
WHERE table_name = 'orders';

💥 踩坑 2:1.8 亿行的 orders 表迁移到一半卡死

现象:orders 表数据量太大,chameleon 单线程导出到第 8000 万行左右报 killed

排查:chameleon 默认的 fetch 机制是游标读取,但是插到 openGauss 是单线程 INSERT,写不赢读的,大量行堆积在内存里,被 OOM killer 干掉了。

# dmesg 看到的真相
[86310.123456] oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, pid=23456 (chameleon)

解决:拆分成按 ID 范围分批导出,每批 500 万行:

# 分批脚本
for i in $(seq 0 36); do
  offset=$((i * 5000000))
  chameleon start_migration mysql_to_opengauss.yaml \
    --table orders \
    --where "id > ${offset} AND id <= $((offset + 5000000))"
done

同时调整 chameleon JVM 参数(DataKit 也是 Java 写的):

export JAVA_OPTS="-Xms4g -Xmx8g -XX:+UseG1GC"

每批跑完后手动 VACUUM ANALYZE 一下,防止事务膨胀。


Step 2:增量同步 — DataKit

全量迁移完后,我们用 DataKit 开启增量同步。MySQL 端开启 Binlog,Oracle 端使用 LogMiner。

# MySQL 开启 Binlog(my.cnf 配置)
server-id = 100
log_bin = mysql-bin
binlog_format = ROW        # 必须 ROW 模式
binlog_row_image = FULL    # 必须 FULL
expire_logs_days = 7

# Oracle 开启归档模式
SQL> ALTER DATABASE ARCHIVELOG;
SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

DataKit 配置增量任务:

# datakit_incremental.yaml
task_name: order_inc_sync
source:
  type: mysql
  connector: binlog
  host: 192.168.1.100
  port: 3306
  start_position:
    file: mysql-bin.000245
    pos: 12893745

target:
  type: opengauss
  
filters:
  - type: dml
    tables:
      - order_db.orders
      - order_db.order_items

增量同步延迟监控:

-- 在 openGauss 端查看同步延迟
SELECT now() - last_received_heartbeat AS sync_delay_seconds
FROM datakit_heartbeat;

💥 踩坑 3:主键冲突的暗雷

现象:双跑期间,应用的写流量同时发给 MySQL 和 openGauss,但 openGauss 报了主键冲突。

排查:发现 MySQL 的 AUTO_INCREMENT 自增步长和 openGauss 的序列不一致。MySQL 是 AUTO_INCREMENT = 1, INCREMENT = 1,但 openGauss 的序列默认 cache = 20,在双写情况下两边分配的 ID 互相冲突。

解决:把 openGauss 序列的 cache 设为 1,并设置起始值大于 MySQL 当前最大值:

-- 停用原有序列,重建
DROP SEQUENCE IF EXISTS orders_id_seq;
CREATE SEQUENCE orders_id_seq 
    START WITH 200000000   -- 大于 MySQL 当前最大 ID
    INCREMENT BY 1
    CACHE 1;               -- 不用缓存,避免双写冲突

ALTER TABLE orders ALTER COLUMN id SET DEFAULT nextval('orders_id_seq');

💥 踩坑 4:Oracle 的 NULL 和空字符串

现象:Oracle 迁移到 openGauss,varchar2 字段值为 ''(空字符串)的迁移后变成了 NULL,业务侧部分接口逻辑判断出错。

原因:Oracle 把空字符串当作 NULL,但 openGauss(遵循 PostgreSQL 语义)区分空字符串和 NULL。迁移时默认行为是一致的,但有些字段在 Oracle 端本身就是 '',被当作 NULL 插入了,业务没预期。

解决:对关键 varchar2 字段在迁移后的 openGauss 上做补丁:

UPDATE financial_records 
SET remark = '' 
WHERE remark IS NULL 
  AND original_source = 'oracle';

当然更好的办法是在迁移前做数据清洗,把所有''显式标记出来。我们当时时间紧,只能事后补。


Step 3:数据校验

迁移完不验证等于白做。我们用 DataKit 自带的数据校验功能和自写脚本双重确认。

-- 行数比对(最简单的兜底)
SELECT 'mysql' as source, COUNT(*) as row_count FROM orders
UNION ALL
SELECT 'opengauss', COUNT(*) FROM orders;

-- Checksum 校验(更严谨)
-- MySQL 端
SELECT SUM(CRC32(CONCAT_WS(',', id, user_id, total_amount, status, created_at)))
FROM orders;

-- openGauss 端(MySQL 的 CRC32 函数在 openGauss 里没有,用 md5 代替)
SELECT SUM(CAST(('x' || md5(CONCAT_WS(',', id::text, user_id::text, 
    total_amount::text, status, created_at::text))) AS BIT(32))::BIGINT)
FROM orders;

HWG-001-mysql-oracle-to-opengauss-migration_diagram_2.png

📊 性能对比:迁移前后

订单中心(MySQL → openGauss)

指标 MySQL 5.7 openGauss 5.0(默认) openGauss 5.0(调优后)
TPS(写入,32 并发) 8,500 7,200 ↓ 15% 9,800 ↑ 15%
QPS(复杂查询,16 并发) 3,200 3,800 ↑ 19% 5,200 ↑ 63%
P99 延迟(写入) 45ms 52ms 38ms
备份速度(全量) 23 min 19 min ↑ 17% 19 min

调优参数(鲲鹏服务器上实测有效):

# postgresql.conf 核心参数
max_connections = 2000
shared_buffers = 8GB            # 物理内存的 25%
work_mem = 64MB
maintenance_work_mem = 2GB
effective_cache_size = 24GB

# openGauss 特有参数
enable_nestloop = off           # 强制走 hash join,对 OLTP 更友好
enable_seqscan = off            # 强制走索引扫描
cstore_insert_mode = fast       # 列存储快速导入模式
numa_distribute_mode = 'all'    # 鲲鹏 NUMA 亲和调度

财务系统(Oracle → openGauss)

指标 Oracle 11g openGauss 5.0(调优后)
月度结算跑批(存储过程) 47 min 41 min ↑ 13%
复杂报表查询(多表 JOIN) 8.2s 6.5s ↑ 21%
数据导入(1 亿行,COPY 方式) 12 min 9 min ↑ 25%

Oracle 的 PL/SQL 存储过程迁移到 openGauss 的 P/L 函数是比较痛苦的,我们踩了一些函数兼容性的坑,这部分会在第二篇单独讲。


❓ 常见问题

Q1:chameleon 和 DataKit 到底选哪个?

建议全量用 chameleon(脚本可控)+ 增量用 DataKit(可视化监控)。如果数据量小于 100GB,只用 DataKit 就够了。如果表数量超过 500 张,建议 chameleon + 分批脚本。

Q2:迁移过程中业务可以不停机吗?

对于 MySQL,理论上有 CDC 工具可以实现接近零停机。但我们实践下来,至少需要 15-30 分钟的只读窗口来完成全量快照的一致性保证。Oracle 同理。

Q3:openGauss 的序列和 MySQL auto_increment 行为不同,需要注意什么?

MySQL 的自增 ID 在事务回滚后不会回退,但 openGauss 序列默认 cache = 20,重启后会跳过一截。线上业务如果对 ID 连续性有强要求(比如作为排序依据),需要做额外处理。

Q4:迁移后发现性能反而下降了,怎么办?

先做 WDR 报告,看瓶颈在哪里:

-- 在 openGauss 主库执行
SELECT * FROM dbe_perf.statement_history 
WHERE start_time > now() - interval '1 hour'
ORDER BY duration DESC LIMIT 20;

常见原因:缺失索引、统计信息陈旧、NUMA 未亲和、seqscan 没有禁用。按上文的参数配置调优大多能解决。


✅ 迁移检查清单(可直接复用)

【迁移前】
□ 源库表结构导出,检查字段类型、自增列、默认值
□ 字符集统一(推荐 UTF-8)
□ 确认所有表都有主键(CDC 工具需要)
□ 评估数据量,选择工具类型

【迁移中】
□ 全量阶段分批执行(每批不超过 5GB)
□ 增量阶段监控延迟 < 5s
□ 类型映射手动校验(特别是 tinyint、datetime、enum)
□ 定期 VACUUM ANALYZE,防止膨胀

【迁移后】
□ 行数 + Checksum 双重校验
□ 业务回放压测(至少 1 小时,覆盖所有核心接口)
□ 双跑验证不少于 7 天(推荐 14 天)
□ 备份策略更新(openGauss 用 gs_dump 或 OBS 归档)
□ 监控告警切换(AOM 接入 openGauss 指标)

📝 总结

这次迁移最深的几点体会:

  1. 工具只是辅助,类型映射才是核心。chameleon 和 DataKit 都能跑通基本流程,但真正花时间的全是在处理 tinyint(1)、datetime 精度、空字符串这些"小事"。

  2. 双跑机制不能省。14 天的双跑帮我们抓到了 7 处业务逻辑差异,大部分是日期处理和 NULL 判断的边界问题。如果直接切过去,上线后排查指定崩溃。

  3. 鲲鹏 + openGauss 的组合在 OLAP 场景下提升明显。复杂查询比 MySQL 快 63%,比 Oracle 快 21%。但 OLTP 写入需要调优,默认配置下甚至不如 MySQL 5.7。

  4. 存储过程迁移是最痛苦的。Oracle 的 PL/SQL 和 openGauss 的 P/L 函数是"看起来像,用起来吐血"的关系,这部分我们花了整个项目 1/3 的时间,下一篇文章专门写。


💬 互动:你们团队在做信创数据库迁移吗?用的是 openGauss 还是其他国产数据库?评论区聊聊踩过的坑。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。