MySQL到PG迁移指南:CI流水线实现平滑切换

2026-06-04阅读 0热度 0
其他

从MySQL迁移到PostgreSQL这事儿,表面上看好像只是换个数据库软件,但真正上手做过的人都知道,这里面的坑远比想象中要多。

AUTO_INCREMENT要改成SERIAL,IFNULL得换成COALESCE,GROUP_CONCAT对应的是string_agg,更别提分区表语法完全两码事、存储过程逻辑要一条条适配、各种索引和外键约束一个都不能错——只要有一个环节出了岔子,数据就可能丢了、查不出来、甚至根本写不进去。

今天聊的这个开源项目MySQL2PG,其核心思路是:用一套成体系的自动化测试流程,把“心跳式迁移”变成“安心迁移”。说白了,就是把大量潜在的迁移问题提前暴露在CI流水线里,而不是等到生产环境出问题才去排查。

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

create_table.sql文件中,定义了167个测试表案例,按覆盖范围分成了五大类。

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

这一组主要覆盖数值、字符集、JSON、时间、默认值、自增、约束、生成列、保留字和命名风格等基本要素。

测试范围表案例覆盖内容
整数类型case_01tinyint/smallint/mediumint/int/bigint/integer,含精度变体
布尔类型case_02TINYINT(1) → BOOLEAN,大小写不敏感
浮点数类型case_03float/double/decimal/numeric/real,含精度和标度
字符集类型case_04~07utf8/utf8mb4/latin1/utf16/ascii,含排序规则
JSON类型case_08json字段,支持嵌套结构
日期时间类型case_09date/time/datetime/timestamp/year,含精度变体
默认值变体case_10数值默认值、字符串默认值、CURRENT_TIMESTAMP
自增类型case_11AUTO_INCREMENT,多自增字段处理
无符号类型case_12unsigned/zerofill,无符号整数转换
枚举和集合case_13enum/set → VARCHAR(255)
二进制类型case_14binary/varbinary/blob/longblob/mediumblob/tinyblob → BYTEA
表选项case_15ROW_FORMAT/COLLATE/CHARSET 等表级选项
分区表case_16RANGE 分区,按年份分区
临时表case_17TEMPORARY TABLE 处理
引号标识符case_18反引号引用标识符
注释类型case_19列注释和表注释
约束类型case_20PRIMARY KEY/UNIQUE KEY/INDEX 复合约束
虚拟列case_21GENERATED ALWAYS AS VIRTUAL
空间类型case_22geometry/point/linestring/polygon 等
怪异语法case_23INTEGER(10)/DOUBLE PRECISION(10,2) 等非标准语法
边缘情况case_24混合字符集、自增主键、longblob
MySQL 8.0保留字case_25rank/system/groups/window/function/role/admin
不可见列case_26INVISIBLE COLUMN 和不可见索引
检查约束case_27CHECK (age > 18) 约束
函数索引case_28MySQL 8.0 表达式索引
默认值变体case_29char/json 默认值
字符集和排序规则case_30utf8mb4_general_ci/utf8mb4_bin
系统表模拟case_31模拟 mysql.db 表结构
复杂生成列case_32CASE WHEN 表达式的生成列
降序索引case_33DESC 索引,混合方向主键
表选项case_34ENGINE=InnoDB 显式指定
枚举字符集case_35enum/set 带字符集和排序规则
大写表名case_36UPPERCASE 表名和列名
驼峰命名case_37ProductId/ProductName/LastUpdate
蛇形命名case_38product_id/product_name/last_update
下划线命名case_39带下划线的命名风格
默认值case_40各种默认值变体

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

这一组把目光投向外键、全文索引、空间索引、复合主键、存储引擎、分区策略、表复制、压缩表、统计信息等更进阶的特性。

测试范围表案例覆盖内容
外键约束case_41父子表关联,ON DELETE CASCADE/ON UPDATE SET NULL
全文索引case_42FULLTEXT KEY,支持自然语言搜索
空间索引case_43SPATIAL INDEX,地理空间数据
复合主键case_44多列组合主键
存储生成列case_45STORED/VIRTUAL 生成列
MyISAM引擎case_46MyISAM 存储引擎转换
MEMORY引擎case_47MEMORY 存储引擎转换
索引类型case_48BTREE/HASH 索引
LIST分区case_49PARTITION BY LIST
HASH分区case_50PARTITION BY HASH,4个分区
表复制LIKEcase_51CREATE TABLE ... LIKE
表复制AScase_52CREATE TABLE ... AS SELECT
延迟约束case_53延迟约束验证
表空间case_54TABLESPACE 指定
压缩表case_55ROW_FORMAT=COMPRESSED
加密表case_56透明数据加密
列级权限case_57敏感数据列权限
子分区case_58RANGE + HASH 复合分区,12个子分区
复杂生成列case_59多函数表达式生成列
统计信息case_60STATS_PERSISTENT/STATS_AUTO_RECALC
大量列case_6160 列宽表,覆盖所有MySQL类型
各种默认值case_62ON UPDATE CURRENT_TIMESTAMP
字符集排序规则case_63多字符集混合
BIT类型case_64bit(1)/bit(8)/bit(16)/bit(32)/bit(64)
YEAR类型case_65year(4)/year 默认值
空间子类型case_66geometry/point 带注释
触发器模拟case_67created_at/updated_at 自动更新
视图模拟case_68模拟视图结构的表
深层嵌套JSONcase_69config/tags/metadata JSON字段
utf8mb4_900排序case_70MySQL 8.0 新排序规则
函数索引case_71concat 表达式索引
检查约束正则case_72CHECK (email LIKE '%@%')
混合生成列case_73STORED + VIRTUAL 混合
混合可见性列case_74可见/不可见列混合
降序主键case_75ASC/DESC 混合方向主键
BLOB前缀索引case_76BLOB 前10字节索引
TEXT前缀索引case_77TEXT 前20字符索引
多列唯一NULLcase_78唯一约束允许NULL
SERIAL默认值case_79SERIAL 别名
ON UPDATE时间戳case_80datetime ON UPDATE CURRENT_TIMESTAMP

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

SRID、长标识符、高精度数值、多值索引、窗口函数、JSON_TABLE、锁相关语法……这些都是实践中容易踩坑的地方。

测试范围表案例覆盖内容
空间SRIDcase_81geometry 带 SRID
宽表case_8210列相似列
长标识符case_8364字符最大长度列名
保留字引用case_84select/update/delete/insert 保留字
高精度数值case_85decimal(65,30) 最大精度
时间类型混合case_86多种时间类型混合
文本二进制混合case_87text/blob 混合
数值边界case_88最大/最小数值
建表方式case_89LIKE/AS/ENGINE 多种建表方式
多值索引case_90MySQL 8.0 多值索引
窗口函数case_91ROW_NUMBER/RANK 窗口函数
JSON_TABLEcase_92MySQL 8.0 JSON_TABLE
锁语法case_93SELECT ... FOR UPDATE
CTEcase_94WITH 子句
递归CTEcase_95递归查询
LIST COLUMNS分区case_96PARTITION BY LIST COLUMNS
RANGE COLUMNS分区case_97PARTITION BY RANGE COLUMNS
KEY分区case_98PARTITION BY KEY
LINEAR HASH分区case_99PARTITION BY LINEAR HASH
优化器提示case_100FORCE INDEX/USE INDEX

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

从电商、CMS、财务、社交到医疗、酒店、餐厅,这些测试表模拟了真实业务场景下的建表逻辑。

业务场景表案例说明
电商系统case_121~130订单、商品、用户、购物车、支付
CMS系统case_131~135文章、分类、标签、评论
财务系统case_136~140账户、交易、报表
社交网络case_141~145用户、好友、动态、点赞
日志系统case_146~148访问日志、错误日志
医疗系统case_149~150患者、病历、处方
酒店管理case_151~152客房、预订、入住
餐厅系统case_153~155菜品、订单、评价

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

复合外键、JSON生成列、时间类型组合、文本二进制混合、数值边界、建表方式专项——专门用来兜住那些容易被忽略的角落。

测试范围表案例覆盖内容
复合外键case_156多列复合外键约束
JSON生成列case_157JSON 字段生成列
时间类型组合case_158datetime(6)/timestamp(6) 组合
文本二进制混合case_159text/blob/varbinary 混合
数值边界case_160decimal/numeric 边界值
建表方式专项case_161~167LIKE/AS/ENGINE/CHARSET 组合测试

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

分区表的迁移向来是重灾区。create_comments_partition_table.sql专门拿来做这件事。

case_169_merge:RANGE分区(单分区)

基础RANGE分区示例,只有一个分区,主键必须包含分区键issue_id,存储引擎指定为InnoDB。适用于按整数范围做简单数据划分的场景。

test_partition_170_range_int:RANGE分区(多分区)

经典多分区模式,包含5个分区,范围从1000到MAXVALUE,主键包含分区键。适用于连续整数范围的均匀数据分布——时间序列数据、ID范围分片,基本都是这个套路。

test_partition_172_list_int:LIST分区

按离散值列表划分,分区键是状态字段。p0包含0,p1包含1,p2包含2和3。主键必须包含status。需要注意的是,LIST分区不支持DEFAULT分区,插入不在列表中的值会直接报错。

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

分区范围呈指数增长——1000、5000、10000、50000、MAXVALUE。这种非均匀策略适合数据分布不均衡的场景,早期范围小后期范围大,同时包含TEXT字段来测试大字段在分区表中的兼容性。

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

create_unique_key_table.sql专门验证唯一键约束在迁移时是否出问题。

类型说明测试表
普通表不带唯一索引mpp_case_normal
单列唯一索引约束单个字段唯一性mpp_case_unique_single
多列唯一索引约束多字段组合唯一性mpp_case_unique_multi
普通索引不应触发分布式约束mpp_case_non_unique_idx
主键+唯一索引混合验证唯一索引列是否进入分布键mpp_case_pk_plus_unique
多个唯一索引验证分布键在多唯一约束场景下的调整mpp_case_multi_unique_paths
宽表复合索引验证普通复合索引不会触发分布键mpp_case_non_unique_composite

迁移到PostgreSQL或Greenplum时,有几个注意事项:UNIQUE INDEX对应CREATE UNIQUE INDEX;分区表唯一键必须包含分区键;分布式表要留意数据分布策略;NULL值处理方面,MySQL和PostgreSQL都允许多个NULL,这块倒是相通的。

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

create_index.sql覆盖了你能想到的大部分索引类型。

基础索引类型包括单列索引、复合索引、前缀索引。复合索引支持最左前缀匹配,前缀索引可以减少索引大小——这些在迁移时都有对应的处理逻辑。

特殊类型索引涵盖全文索引(FULLTEXT,支持自然语言和布尔搜索)、空间索引(SPATIAL,地理空间数据查询)和函数索引(基于表达式创建)。

分区表索引有RANGE、LIST、HASH三种,外加子分区索引。

存储引擎相关索引则区分InnoDB(聚簇索引,支持事务)、MyISAM(非聚簇索引)和MEMORY(内存表索引,数据易失)。

特殊场景索引覆盖了压缩表索引、不同字符集和排序规则的索引、生成列索引等边角情况。

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

create_view.sql定义了42个测试视图,按复杂度分成五个等级:简单(单表查询,5个)、中等(多表连接,3个)、复杂(子查询和聚合函数,3个)、高级(窗口函数和JSON操作,6个),以及MySQL 8.0特高级视图(20个)。

从简单的view_case01_simple_integers到综合了CTE、JSON_TABLE、正则表达式、GIS空间函数的view_case41_mysql8_ultimate,基本覆盖了视图迁移可能遇到的所有语法场景。

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

create_function.sql定义了110个复杂的存储函数。每个函数2到30行代码,覆盖MySQL存储过程的核心语法,有的涉及多达10张表的关联查询。

前100个函数(func_001 ~ func_100)侧重语法转换测试,从分区表、枚举类型、JSON操作到全文检索、空间数据,每个函数都有明确的测试目标。后10个(func_101 ~ func_110)则偏向业务场景,比如复合主外键聚合计算、JSON字段提取、时间类型格式化、高精度数值聚合——这些都是从实际项目中提炼出来的需求。

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

有测试用例还不够。关键是——每次代码提交,怎么确保不会破坏迁移功能?

MySQL2PG的GitHub Actions CI流水线采用了10种数据库版本组合,按顺序执行,避免资源竞争:MySQL 5.7到8.0,分别对应PostgreSQL的12、14、16、17、18版本。

每个Job的流程都一样:启动MySQL容器,启动PostgreSQL容器,等待两个数据库就绪,然后执行SQL脚本初始化测试数据——167张表、每表10行数据、数百个索引、42个视图、110个函数、用户和权限、分区表注释,全套走一遍。接着生成配置文件,编译运行MySQL2PG,最后上传转换日志。

这套流水线确保每次提交都能验证:167张表结构转换、1670行数据同步、数百个索引转换、42个视图语法、110个函数映射、用户权限、分区表处理、唯一键兼容性——在10种MySQL × PostgreSQL版本组合下一一通过。

八、转换能力全景图

把MySQL2PG的转换能力归纳起来,大致可以分为四个维度。

表结构转换(DDL):AUTO_INCREMENT变SERIAL或BIGSERIAL,TINYINT(1)变BOOLEAN,INT和INTEGER保持对应,FLOAT变REAL,DOUBLE变DOUBLE PRECISION,DATETIME变TIMESTAMP,VARCHAR不变,TEXT和LONGTEXT统一变TEXT,BLOB和LONGBLOB变BYTEA,JSON保持JSON,ENUM和SET转为VARCHAR(255),DECIMAL保持,BIT保持,YEAR转INTEGER,UNSIGNED则直接移除改为有符号类型。

视图函数转换(50+函数映射):IFNULL变COALESCE,IF转CASE WHEN,GROUP_CONCAT变string_agg,CONCAT改用||操作符,NOW变CURRENT_TIMESTAMP,DATE_FORMAT变to_char,STR_TO_DATE变to_date,UNIX_TIMESTAMP变extract(epoch from ...),JSON函数群对应到jsonb的各种操作——json_build_object、jsonb_set、jsonb_array_length,等等。

索引转换:主键、唯一索引、普通索引一一对应。全文索引转为tsvector配合GIN索引,空间索引依赖PostGIS扩展,前缀索引用表达式索引模拟,函数索引同样转为表达式索引。

分区表处理:RANGE和LIST分区转为PostgreSQL的声明式分区,HASH也对应声明式,KEY分区转为HASH,LINEAR HASH转为普通HASH,子分区转为复合分区,LIST COLUMNS和RANGE COLUMNS分别转化为LIST和RANGE。

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

迁移完成后,工具会自动执行数据验证:MySQL行数和PostgreSQL行数逐一对比。只要有一张表的行数对不上,CI流水线就会直接亮红灯,阻止有问题的代码合并。

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

有了全面的测试用例,代码本身的稳定性如何保障?MySQL2PG建立了一套三层测试体系。

第一层:单元测试。覆盖核心转换逻辑的每个细节:配置解析、视图函数转换、函数语法映射、数据同步、DDL转换、报告生成、PostgreSQL连接管理。以视图函数转换测试为例,有88个测试用例,验证IFNULL、ROUND、MOD、DATE_FORMAT、JSON函数群的转换是否正确。当前代码覆盖率达到98%。

第二层:集成测试。在真实的MySQL和PostgreSQL环境中,验证端到端迁移流程。84个测试用例覆盖连通性、DDL转换、数据同步、视图转换、索引转换、函数转换、用户权限、运行选项、边界场景和错误处理。每个用例都验证退出码、日志、表结构、数据行数、视图和函数的可用性、索引完整性,以及权限配置。

第三层:CI流水线自动化测试。每次代码提交自动触发,10种数据库版本组合。配备了Race Detector检测并发数据竞争,持续生成覆盖率报告(88%+),有编译检查和静态分析。分支保护机制确保main分支必须有CI通过才能合并,每次发布都经过完整测试流程。

总结下来,这套体系的核心逻辑就四句话:

第一,全覆盖的测试用例——167张表、42个视图、110个函数、4种分区策略、6种唯一键场景、5大类索引,把从基础语法到业务场景的迁移需求全部兜住。

第二,三层测试防护——200+单元测试(98%覆盖率)、84个集成测试、10种CI版本组合,确保代码稳定可靠。

第三,自动化的CI流水线——每次提交自动验证,失败了就不让合并。

第四,严格的数据验证——迁移前后行数对比,数据一致性不放松。

数据库迁移不是赌运气。它靠的是体系化的测试、自动化的流程和严格的代码质量保障。MySQL2PG在这个方向上,提供了一个相当扎实的参考样本。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策