首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MySQL2PG 如何使用CI流水线确保MySQL到PG的平滑迁移

MySQL2PG 如何使用CI流水线确保MySQL到PG的平滑迁移

作者头像
小徐
发布2026-06-03 20:23:00
发布2026-06-03 20:23:00
520
举报
文章被收录于专栏:GreenplumGreenplum

每一个测试用例背后,都是一次真实迁移场景的缩影

MySQL 要迁到 PostgreSQL,表面上只是换个数据库,实际上:

  • • 表结构语法不兼容,AUTO_INCREMENT 要改成 SERIAL
  • • 分区表语法差异,RANGE/LIST/HASH 分区策略完全不同
  • • 视图定义里的 IFNULLGROUP_CONCAT、JSON 函数全部要转换
  • • 存储函数里的游标、变量、流程控制逻辑要逐一适配
  • • 外键约束、唯一键、全文索引、空间索引……一个都不能错

只要有一个环节出错,数据就可能丢、查不出、或者写不进去。

今天介绍的这个开源项目 MySQL2PG,用一套全覆盖的测试体系 + CI 自动化流水线,使用大量的测试案例,把"心跳迁移"变成了"安心迁移",确保了迁移期间无异常,顺利过度。

一、167 张测试表:从基础类型到业务场景的全覆盖

create_table.sql 文件包含了 167 个测试表案例,按覆盖范围分为 5 大类:

第一类:基础类型与DDL语法(case_01 ~ case_40)

覆盖数值、字符集/排序规则、JSON、时间、默认值、自增、约束、生成列、保留字、命名风格等。

测试范围

表案例

覆盖内容

整数类型

case_01

tinyint/smallint/mediumint/int/bigint/integer,含精度变体

布尔类型

case_02

TINYINT(1) → BOOLEAN,大小写不敏感

浮点数类型

case_03

float/double/decimal/numeric/real,含精度和标度

字符集类型

case_04~07

utf8/utf8mb4/latin1/utf16/ascii,含排序规则

JSON类型

case_08

json字段,支持嵌套结构

日期时间类型

case_09

date/time/datetime/timestamp/year,含精度变体

默认值变体

case_10

数值默认值、字符串默认值、CURRENT_TIMESTAMP

自增类型

case_11

AUTO_INCREMENT,多自增字段处理

无符号类型

case_12

unsigned/zerofill,无符号整数转换

枚举和集合

case_13

enum/set → VARCHAR(255)

二进制类型

case_14

binary/varbinary/blob/longblob/mediumblob/tinyblob → BYTEA

表选项

case_15

ROW_FORMAT/COLLATE/CHARSET 等表级选项

分区表

case_16

RANGE 分区,按年份分区

临时表

case_17

TEMPORARY TABLE 处理

引号标识符

case_18

反引号引用标识符

注释类型

case_19

列注释和表注释

约束类型

case_20

PRIMARY KEY/UNIQUE KEY/INDEX 复合约束

虚拟列

case_21

GENERATED ALWAYS AS VIRTUAL

空间类型

case_22

geometry/point/linestring/polygon 等

怪异语法

case_23

INTEGER(10)/DOUBLE PRECISION(10,2) 等非标准语法

边缘情况

case_24

混合字符集、自增主键、longblob

MySQL 8.0保留字

case_25

rank/system/groups/window/function/role/admin

不可见列

case_26

INVISIBLE COLUMN + 不可见索引

检查约束

case_27

CHECK (age > 18) 约束

函数索引

case_28

MySQL 8.0 表达式索引

默认值变体

case_29

char/json 默认值

字符集和排序规则

case_30

utf8mb4_general_ci/utf8mb4_bin

系统表模拟

case_31

模拟 mysql.db 表结构

复杂生成列

case_32

CASE WHEN 表达式的生成列

降序索引

case_33

DESC 索引,混合方向主键

表选项

case_34

ENGINE=InnoDB 显式指定

枚举字符集

case_35

enum/set 带字符集和排序规则

大写表名

case_36

UPPERCASE 表名和列名

驼峰命名

case_37

ProductId/ProductName/LastUpdate

蛇形命名

case_38

product_id/product_name/last_update

下划线命名

case_39

带下划线的命名风格

默认值

case_40

各种默认值变体

第二类:索引/约束与表特性(case_41 ~ case_80)

覆盖外键、全文、空间、复合主键、存储引擎、分区、复制建表、压缩、统计信息等。

第三类:边界语法与MySQL 5.7/8.0特性(case_81 ~ case_120)

覆盖SRID、长标识符、高精度数值、多值索引、窗口函数、JSON_TABLE、锁相关语法等。

第四类:业务化建模样例(case_121 ~ case_155)

覆盖电商、CMS、财务、社交、日志、医疗、酒店、餐厅等真实业务场景。

第五类:新增综合增强场景(case_156 ~ case_167)

覆盖复合外键、JSON生成列、时间类型组合、文本二进制混合、数值边界、建表方式专项。

二、分区表专项测试:4种分区策略全覆盖

create_comments_partition_table.sql 文件专门测试分区表的迁移兼容性:

1. case_169_merge:RANGE分区(单分区)

代码语言:javascript
复制
-- 特点:
-- 1. 基础 RANGE 分区示例,仅包含一个分区
-- 2. 主键必须包含分区键 issue_id
-- 3. 使用 ENGINE = InnoDB 指定存储引擎
-- 4. 适用于按整数范围进行简单数据划分的场景
-- 5. row_format=dynamic 支持动态行格式

2. test_partition_170_range_int:RANGE分区(多分区)

代码语言:javascript
复制
-- 特点:
-- 1. 经典 RANGE 分区模式,包含 5 个分区
-- 2. 分区范围递增:1000, 2000, 3000, 10000, MAXVALUE
-- 3. 使用 MAXVALUE 作为最后一个分区的边界
-- 4. 主键包含分区键,满足 MySQL 分区约束
-- 5. 适用于按连续整数范围均匀分布数据的场景
-- 6. 常用于时间序列数据、ID 范围分片等场景

3. test_partition_172_list_int:LIST分区

代码语言:javascript
复制
-- 特点:
-- 1. LIST 分区模式,按离散值列表进行分区
-- 2. 分区键为状态字段,支持业务状态分类
-- 3. 分区值为离散集合:p0(0), p1(1), p2(2,3)
-- 4. 一个分区可包含多个离散值(如 p2 包含 2 和 3)
-- 5. 主键必须包含分区键 status
-- 6. 适用于按枚举值、状态码等离散值进行数据划分
-- 7. 注意:LIST 分区不支持 DEFAULT 分区,插入不在列表中的值会报错

4. test_partition_173_range_multi:RANGE分区(非均匀分布)

代码语言:javascript
复制
-- 特点:
-- 1. RANGE 分区模式,包含 5 个分区
-- 2. 分区范围呈指数增长:1000, 5000, 10000, 50000, MAXVALUE
-- 3. 非均匀分区策略,适合数据分布不均匀的场景
-- 4. 早期分区范围小,后期分区范围大
-- 5. 包含 TEXT 类型字段,测试大字段在分区表中的兼容性
-- 6. 主键包含分区键 issue_id
-- 7. 适用于数据量随时间增长的业务场景

三、唯一键测试:6 种唯一约束场景

create_unique_key_table.sql 文件专门验证唯一键约束在迁移时的兼容性:

唯一键类型分类

PostgreSQL/Greenplum迁移注意事项

  • • UNIQUE INDEX → CREATE UNIQUE INDEX
  • • 分区表唯一键必须包含分区键
  • • 分布式表需注意数据分布策略
  • • NULL 值处理:MySQL允许多个NULL,PostgreSQL也允许

四、索引全覆盖:5大类索引测试

create_index.sql 文件包含以下索引类型:

1. 基础索引类型

  • 单列索引:最基本的索引类型,加速单列查询
  • 复合索引:多列组合索引,支持索引覆盖和最左前缀匹配
  • 前缀索引:对字符串前缀创建索引,减少索引大小

2. 特殊类型索引

  • 全文索引 (FULLTEXT):支持自然语言搜索和布尔搜索
  • 空间索引 (SPATIAL):支持地理空间数据查询
  • 函数索引:基于函数或表达式创建的索引

3. 分区表索引

  • RANGE分区索引:按范围分区的表索引
  • LIST分区索引:按离散值列表分区的表索引
  • HASH分区索引:按哈希值分区的表索引
  • 子分区索引:复合分区策略的索引

4. 存储引擎相关索引

  • InnoDB索引:聚簇索引,支持事务
  • MyISAM索引:非聚簇索引,不支持事务
  • MEMORY索引:内存表索引,数据易失

5. 特殊场景索引

  • 压缩表索引:ROW_FORMAT=COMPRESSED的表索引
  • 字符集索引:不同字符集字段的索引
  • 排序规则索引:不同collation的索引
  • 生成列索引:虚拟列或存储生成列的索引

五、42 个测试视图:5个复杂度等级

create_view.sql 定义了 42 个测试视图,按复杂度分为 5 个等级:

视图列表

视图复杂度分类

  1. 1. 简单视图(单表查询):5个
  2. 2. 中等复杂度视图(多表连接):3个
  3. 3. 复杂视图(子查询、聚合函数):3个
  4. 4. 高级视图(窗口函数、JSON操作):6个
  5. 5. MySQL 8.0特高级视图:20个

六、110 个存储函数:最多涉及10表关联

create_function.sql 定义了 110 个复杂的存储函数,每个函数都有明确的测试目标:

复杂分析函数(func_001 ~ func_100)

业务场景函数(func_101 ~ func_110)

每个函数 30~200 行代码,覆盖了 MySQL 存储过程的核心语法。

七、CI流水线:10种数据库版本组合的自动化测试

有了测试用例还不够,怎么确保每次代码提交都不会破坏迁移功能?

MySQL2PG 的 GitHub Actions CI 流水线给出了答案:

测试执行顺序

CI 流水线按顺序执行 10 种数据库版本组合,避免资源竞争:

代码语言:javascript
复制
MySQL 5.7 → PostgreSQL 12  ↓
MySQL 5.7 → PostgreSQL 14  ↓
MySQL 5.7 → PostgreSQL 16  ↓
MySQL 5.7 → PostgreSQL 17  ↓
MySQL 5.7 → PostgreSQL 18  ↓
MySQL 8.0 → PostgreSQL 12  ↓
MySQL 8.0 → PostgreSQL 14  ↓
MySQL 8.0 → PostgreSQL 16  ↓
MySQL 8.0 → PostgreSQL 17  ↓
MySQL 8.0 → PostgreSQL 18

每个测试 Job 的执行流程

每个 Job 都包含以下步骤:

代码语言:javascript
复制
1. 启动MySQL容器(5.7或8.0)
2.启动PostgreSQL容器(12/14/16/17/18)
3.等待两个数据库就绪(healthcheck)
4.执行SQL脚本初始化测试数据:
   ├──create_table.sql          # 167 张表
   ├──insert_data.sql           # 每张表 10 条测试数据
   ├──create_index.sql          # 数百个索引
   ├──create_view.sql           # 42 个视图
   ├──create_function.sql       # 110 个函数
   ├──create_user.sql           # 用户和权限
   └──create_comments_partition_table.sql# 分区表注释
5.生成config.yml配置文件
6.编译并运行mysql2pg工具
7. 上传转换日志(失败时)

测试覆盖率

CI 流水线确保:

  • • ✅ 167 张表的结构转换
  • • ✅ 1670 行数据的同步验证(每张表 10 行)
  • • ✅ 数百个索引的转换(主键、唯一键、普通索引、全文索引)
  • • ✅ 42 个视图的语法转换
  • • ✅ 110 个函数的语法映射
  • • ✅ 用户和权限的转换
  • • ✅ 分区表的处理(RANGE/LIST/HASH/子分区)
  • • ✅ 唯一键的兼容性验证
  • • ✅ 10 种 MySQL × PostgreSQL 版本组合

八、转换能力全景图

MySQL2PG 的转换能力可以总结为以下几个维度:

1. 表结构转换(DDL)

2. 视图函数转换(50+ 函数映射)

3. 索引转换

4. 分区表处理

九、数据验证:确保一行都不丢

迁移完成后,MySQL2PG 会自动执行数据验证

代码语言:javascript
复制
✅ 转换表结构:167 张表
✅ 同步表数据:1670 行
✅ 转换索引:数百个
✅ 转换视图:42 个
✅ 转换函数:110 个
✅ 数据验证:MySQL 行数 = PostgreSQL 行数

如果任何一张表的行数不一致,CI 流水线会立即失败,阻止有问题的代码合并。

十、代码测试体系:确保每一次迁移都能成功

有了全面的测试用例还不够,代码本身的稳定性和可靠性如何保障?

MySQL2PG 建立了三层测试体系,确保每一行代码都经过严格验证:

第一层:单元测试(Unit Tests)

单元测试是代码质量的第一道防线,覆盖核心转换逻辑的每个细节:

测试覆盖范围

关键测试场景

1. 视图函数转换测试(88+ 测试用例)

代码语言:javascript
复制
// 示例:IFNULL 转换测试
tests := []struct{
    name     string
    input    string
    expected string
}{
    {"IFNULL 单参数", "IFNULL(a)", "COALESCE(a)"},
    {"IFNULL 双参数", "IFNULL(a, b)", "COALESCE(a, b)"},
    {"IFNULL 嵌套", "IFNULL(IFNULL(a, b), c)", "COALESCE(COALESCE(a, b), c)"},
    {"IFNULL 在视图中", "SELECT IFNULL(col, 0) FROM t", "SELECT COALESCE(col, 0) FROM t"},
}

2. 数值函数转换测试

代码语言:javascript
复制
// ROUND/MOD 转换测试
tests := []struct{
    name     string
    input    string
    expected string
}{
    {"ROUND 单参数", "ROUND(col, 2)", "ROUND(col::NUMERIC, 2)"},
    {"MOD 单参数", "MOD(col, 10)", "MOD(col::NUMERIC, 10)"},
    {"ROUND 嵌套", "ROUND(ROUND(col, 3), 2)", "ROUND(ROUND(col::NUMERIC, 3)::NUMERIC, 2)"},
}

3. 日期时间函数转换测试

代码语言:javascript
复制
// DATE_FORMAT 转换测试
tests := []struct{
    name     string
    input    string
    expected string
}{
    {"DATE_FORMAT 简单", "DATE_FORMAT(dt, '%Y-%m-%d')", "to_char(dt, 'YYYY-MM-DD')"},
    {"DATE_FORMAT 复杂", "DATE_FORMAT(dt, '%Y-%m-%d %H:%i:%s')", "to_char(dt, 'YYYY-MM-DD HH24:MI:SS')"},
}

4. JSON 函数转换测试

代码语言:javascript
复制
// JSON 函数转换测试
tests := []struct{
    name     string
    input    string
    expected string
}{
    {"JSON_EXTRACT", "JSON_EXTRACT(doc, '$.name')", "doc->>'name'"},
    {"JSON_OBJECT", "JSON_OBJECT('key', val)", "json_build_object('key', val)"},
    {"JSON_ARRAY", "JSON_ARRAY(a, b)", "json_build_array(a, b)"},
}
单元测试执行
代码语言:javascript
复制
# 运行所有单元测试
go test -v -race -coverprofile=coverage.out ./...

# 按包运行测试
go test ./internal/config/...
go test ./internal/converter/postgres/...
go test ./internal/postgres/...
go test ./internal/report/...

# 查看覆盖率
go tool cover -html=coverage.out -o coverage.html

当前代码覆盖率:98%+,确保核心转换逻辑的每一行代码都被测试覆盖。

第二层:集成测试(Integration Tests)

集成测试在真实的 MySQL 和 PostgreSQL 环境中验证端到端的迁移流程:

测试矩阵(84 个测试用例)

集成测试执行流程
代码语言:javascript
复制
#!/bin/bash
# 脚本:scripts/integrationtests/run_integration_tests.sh

# 1. 修改 config.yml 配置
cat > config.yml << EOF
mysql:
  host: 127.0.0.1
  port: 3306
  username: root
  password: rootpassword
  database: test_db
postgresql:
  host: localhost
  port: 5432
  username: postgres
  password: postgrespassword
  database: test_db
conversion:
  options:
    tableddl: true
    data: true
    view: true
    indexes: true
    functions: true
    users: true
    table_privileges: true
    validate_data: true
EOF

# 2. 运行 MySQL2PG 工具
./mysql2pg -c config.yml

# 3. 检查退出码
if [ $? -eq 0 ]; then
    echo"✅ PASS"
else
    echo"❌ FAIL"
fi

# 4. 验证 PostgreSQL 数据
psql -h localhost -U postgres -d test_db -c "SELECT COUNT(*) FROM case_01_integers;"
集成测试结果验证

每个集成测试用例都会验证:

  1. 1. 退出码检查:工具是否正常退出(exit code = 0)
  2. 2. 日志检查:conversion.log 中是否有错误
  3. 3. 表结构验证:PostgreSQL 中表结构是否正确转换
  4. 4. 数据行数验证:MySQL 和 PostgreSQL 行数是否一致
  5. 5. 视图验证:视图是否可以正常查询
  6. 6. 函数验证:存储函数是否可以正常调用
  7. 7. 索引验证:索引是否正确创建
  8. 8. 权限验证:用户和权限是否正确转换

第三层:CI 流水线自动化测试

每一次代码提交都会触发 GitHub Actions CI 流水线,自动执行所有测试:

CI 流水线架构
代码语言:javascript
复制
# .github/workflows/go.yml

unit-test:
name:UnitTests
runs-on:ubuntu-latest
steps:
    -uses:actions/checkout@v4
    -uses:actions/setup-go@v5
      with:
        go-version:'1.24'
    -run:gobuild-v./...
    -run:gotest-v-race-coverprofile=coverage.out./internal/config/...
    -run:gotest-v-race-coverprofile=coverage.out./internal/converter/postgres/...
    -run:gotest-v-race-coverprofile=coverage.out./internal/postgres/...
    -run:gotest-v-race-coverprofile=coverage.out./internal/report/...

integration-test-mysql57-pg12:
name:MySQL5.7→PostgreSQL12
needs:unit-test
services:
    mysql:
      image:mysql:5.7
    postgres:
      image:postgres:12
steps:
    -初始化测试数据(167张表+42视图+110函数)
    -运行MySQL2PG工具
    -验证迁移结果

integration-test-mysql57-pg14:
name:MySQL5.7→PostgreSQL14
needs:integration-test-mysql57-pg12
# 同样的测试流程

#  共 10 种版本组合
CI 流水线保障

1. 代码质量保障

  • • ✅ Race Detector:检测并发代码的数据竞争
  • • ✅ 覆盖率报告:确保核心逻辑被测试覆盖(88%+)
  • • ✅ 编译检查:确保代码无语法错误、类型错误
  • • ✅ 静态分析:golint、go vet 等工具检查代码质量

2. 功能验证保障

  • • ✅ 10 种版本组合:MySQL 5.7/8.0 × PostgreSQL 12/14/16/17/18
  • • ✅ 完整测试数据:167 张表 + 1670 行数据 + 42 视图 + 110 函数
  • • ✅ 端到端验证:从 MySQL 读取 → 转换 → PostgreSQL 写入 → 数据验证
  • • ✅ 错误日志上传:失败时自动上传 conversion.log 和 errors.log

3. 发布质量保障

  • • ✅ 分支保护:main 分支必须有 CI 通过才能合并
  • • ✅ 标签发布:每次发布都经过完整测试流程
  • • ✅ 回归测试:新版本不会破坏已有功能
  • • ✅ 兼容性验证:确保新旧版本 MySQL/PostgreSQL 都兼容

测试体系的三层防护

代码语言:javascript
复制
┌─────────────────────────────────────────┐
│  第三层:CI 流水线(10 种版本组合)        │
│  • 每次提交自动执行                       │
│  • 端到端集成测试                         │
│  • 数据验证                              │
├─────────────────────────────────────────┤
│  第二层:集成测试(84 个测试用例)          │
│  • 真实数据库环境                         │
│  • 完整迁移流程                           │
│  • 功能验证                              │
├─────────────────────────────────────────┤
│  第一层:单元测试(98%+ 覆盖率)           │
│  • 核心转换逻辑                           │
│  • 函数映射验证                           │
│  • 边界情况处理                           │
└─────────────────────────────────────────┘

测试数据覆盖

数据类型

数量

说明

测试表

167 张

覆盖所有 MySQL 类型和语法

测试数据

1670 行

每张表 10 行测试数据

测试索引

数百个

主键、唯一键、普通索引、全文索引

测试视图

42 个

从简单视图到 MySQL 8.0 高级特性

测试函数

110 个

最多涉及 10 表关联的复杂逻辑

测试用例

84 个

集成测试覆盖所有配置选项

单元测试

200+

覆盖核心转换逻辑的每个细节

三层测试体系,200+ 单元测试用例,84 个集成测试用例,10 种数据库版本组合,确保每一次代码提交都能稳定迁移。


总结

MySQL2PG 的测试体系可以总结为四句话:

  1. 1. 全覆盖的测试用例:167 张表 + 42 个视图 + 110 个函数 + 4种分区策略 + 6种唯一键场景 + 5大类索引,覆盖从基础语法到业务场景的所有迁移需求
  2. 2. 三层测试防护:200+ 单元测试(88%+ 覆盖率)+ 84 个集成测试 + 10 种 CI 版本组合,确保代码稳定可靠
  3. 3. 自动化的 CI 流水线:每次提交自动验证,失败立即阻止合并
  4. 4. 严格的数据验证:迁移前后行数对比,确保数据一致性

数据库迁移不是赌运气,而是靠体系化的测试、自动化的流程和严格的代码质量保障来确保每一次都能成功。

📦 项目地址:https://github.com/xfg0218/MySQL2PG

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 河马coding 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、167 张测试表:从基础类型到业务场景的全覆盖
    • 第一类:基础类型与DDL语法(case_01 ~ case_40)
    • 第二类:索引/约束与表特性(case_41 ~ case_80)
    • 第三类:边界语法与MySQL 5.7/8.0特性(case_81 ~ case_120)
    • 第四类:业务化建模样例(case_121 ~ case_155)
    • 第五类:新增综合增强场景(case_156 ~ case_167)
  • 二、分区表专项测试:4种分区策略全覆盖
    • 1. case_169_merge:RANGE分区(单分区)
    • 2. test_partition_170_range_int:RANGE分区(多分区)
    • 3. test_partition_172_list_int:LIST分区
    • 4. test_partition_173_range_multi:RANGE分区(非均匀分布)
  • 三、唯一键测试:6 种唯一约束场景
    • 唯一键类型分类
    • PostgreSQL/Greenplum迁移注意事项
  • 四、索引全覆盖:5大类索引测试
    • 1. 基础索引类型
    • 2. 特殊类型索引
    • 3. 分区表索引
    • 4. 存储引擎相关索引
    • 5. 特殊场景索引
  • 五、42 个测试视图:5个复杂度等级
    • 视图列表
    • 视图复杂度分类
  • 六、110 个存储函数:最多涉及10表关联
    • 复杂分析函数(func_001 ~ func_100)
    • 业务场景函数(func_101 ~ func_110)
  • 七、CI流水线:10种数据库版本组合的自动化测试
    • 测试执行顺序
    • 每个测试 Job 的执行流程
    • 测试覆盖率
  • 八、转换能力全景图
    • 1. 表结构转换(DDL)
    • 2. 视图函数转换(50+ 函数映射)
    • 3. 索引转换
    • 4. 分区表处理
  • 九、数据验证:确保一行都不丢
  • 十、代码测试体系:确保每一次迁移都能成功
    • 第一层:单元测试(Unit Tests)
      • 测试覆盖范围
      • 关键测试场景
      • 单元测试执行
    • 第二层:集成测试(Integration Tests)
      • 测试矩阵(84 个测试用例)
      • 集成测试执行流程
      • 集成测试结果验证
    • 第三层:CI 流水线自动化测试
      • CI 流水线架构
      • CI 流水线保障
    • 测试体系的三层防护
    • 测试数据覆盖
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档