PostgreSQL 和 MySQL 都是主流开源关系型数据库,很多人会想当然地认为"反正都是 SQL,改改连接串就行"。但实际上两者在类型系统的严谨程度、SQL 语法风格、并发控制机制、扩展生态上差异极大,PostgreSQL 又是一个功能极其"激进"的数据库(JSONB、数组类型、丰富的约束和扩展),迁移到相对"务实克制"的 MySQL,往往意味着不少功能需要重新设计而不是简单转换。本文按迁移实际流程梳理关键注意事项。
迁移成本高不高,很大程度取决于业务用了多少 PostgreSQL 的"特色功能"。建议先扫描以下几类对象的使用情况:
JSONB 字段做半结构化存储;INTEGER[]、
TEXT[]);ENUM 类型、自定义组合类型(
CREATE TYPE);PostGIS 地理信息、
pg_trgm 模糊搜索、
hstore 等);FDW);PL/pgSQL 写了复杂存储过程/触发器。这些功能里,有些 MySQL 有近似替代(如 JSON 类型),有些完全没有对应物(如 PostGIS、
FDW、继承表),后者需要单独评估重构方案,往往是迁移里工作量最大的部分。
pg_dump 导出+转换+导入即可,流程简单。这是从 PostgreSQL 迁移到 MySQL 时最容易"表面迁完、实际语义变了"的环节。
| PostgreSQL 类型 | MySQL 映射 | 注意事项 |
|---|---|---|
JSONB |
JSON |
MySQL 的
JSON 类型没有 PostgreSQL
JSONB 的二进制存储和丰富的索引能力(如 GIN 索引),涉及 JSON 内字段的高频查询,需要额外拆字段建普通索引或使用生成列(Generated Column)+索引来弥补性能 |
TEXT[] /
INTEGER[] 等数组类型 |
无原生对应 | MySQL 不支持数组类型,通常需要拆成关联表(一对多),或退化为 JSON 存储,这是 结构性改动,往往牵涉表结构重新设计,而不是简单换类型名 |
ENUM |
ENUM /
VARCHAR + 约束 |
两边都有
ENUM,但 PostgreSQL 的枚举是独立类型、可跨表复用,MySQL 的
ENUM 是列属性、不能跨表共享定义,多表复用同一枚举时需要拆成字典表 |
SERIAL /
BIGSERIAL |
AUTO_INCREMENT |
语义上接近,但 PostgreSQL 的序列(
SEQUENCE)可以独立于表存在、被多处引用,
AUTO_INCREMENT 与单表绑定,共享序列场景需要重新设计 |
BOOLEAN |
TINYINT(1) /
BOOLEAN(是 TINYINT 的别名) |
MySQL 没有真正独立的布尔类型,是用
TINYINT(1) 模拟的,注意 ORM 框架和前端对返回值
0/1 的处理是否与原来的
true/false 一致 |
TIMESTAMP WITH TIME ZONE |
TIMESTAMP /
DATETIME |
PostgreSQL 的时区处理语义清晰(存储时自动转 UTC,读取时按会话时区转换),MySQL 的时区行为依赖
TIMESTAMP vs
DATETIME 的选择和
time_zone 会话变量,容易在跨时区部署时出现时间错乱,务必显式测试 |
NUMERIC /
DECIMAL |
DECIMAL |
精度语义基本一致,但要注意 PostgreSQL 的
NUMERIC 可以不指定精度存储任意精度数值,MySQL
DECIMAL 必须显式指定精度和标度 |
| 字符集与排序规则 |
utf8mb4 |
PostgreSQL 常用
UTF8 +
en_US.UTF-8 locale 排序,MySQL 需要显式选择
utf8mb4 字符集及
_unicode_ci/
_0900_ai_ci 等排序规则,中文、emoji 等多字节字符的排序结果可能与原库不同,需要专门验证 |
lower_case_table_names 参数影响),迁移脚本里如果混用了大小写命名,行为可能不一致,建议统一规范后再迁移。RETURNING 子句:PostgreSQL 的
INSERT/UPDATE/DELETE ... RETURNING 非常常用,MySQL 不支持该语法,需要改写为"先执行语句,再用
LAST_INSERT_ID() 或额外
SELECT 查询"的方式获取结果,涉及应用层代码改动。INSERT ... ON CONFLICT DO UPDATE 需要改写为 MySQL 的
INSERT ... ON DUPLICATE KEY UPDATE,两者在冲突判定列的指定方式、语义细节上不完全等价,需要逐条核对。WITH CTE 的支持都不错(MySQL 8.0+),但递归 CTE 写法、
LATERAL 关联子查询等高级特性的支持程度和语法细节仍有差异,需要在低版本 MySQL(5.7 及以下)上特别小心——很多语法根本不支持。||,MySQL 默认用
CONCAT() 函数(
|| 在 MySQL 里默认表示逻辑或,除非开启
PIPES_AS_CONCAT sql_mode),这是极易被忽略的语法差异,批量替换时要用工具辅助而非人工逐个改。LIMIT/OFFSET:两者语法接近,但 PostgreSQL 支持
FETCH FIRST n ROWS ONLY 标准 SQL 写法,迁移时统一改成 MySQL 的
LIMIT 写法即可,风险较低。BEFORE/AFTER/INSTEAD OF,还支持通过函数灵活复用逻辑;MySQL 的触发器功能相对简化,不支持
INSTEAD OF,也没有 PostgreSQL 那种"触发器函数"复用机制,复杂触发器链路需要重新梳理设计。PostGIS(地理空间)、
pg_trgm(模糊匹配)、
hstore(键值存储)等扩展,MySQL 没有直接对应物,需要单独选型替代方案(比如地理位置数据改用 MySQL 8.0 的空间数据类型,模糊搜索改用全文索引或引入 Elasticsearch 等外部系统),这部分往往是迁移项目里最耗时的技术攻关点。READ COMMITTED,MySQL(InnoDB)默认
REPEATABLE READ,隔离级别差异会直接影响并发场景下的锁等待和幻读表现,依赖具体隔离级别语义的业务逻辑需要重新验证。VACUUM 回收)与 MySQL InnoDB 的 MVCC(基于 undo log)机制不同,长事务、大批量更新场景下的资源占用和性能表现不能直接类比,建议做专门的压测。| 工具 | 特点 |
|---|---|
| pg_dump + 自定义转换脚本 | 最基础的方式,灵活但全靠人工把控类型转换和语法改写细节,适合中小规模、可控的迁移 |
| AWS DMS | 支持异构数据库迁移+持续复制(CDC),对 PostgreSQL → MySQL 有一定官方支持,适合云上、需要在线迁移的场景 |
| pg_chameleon | 专门面向 PostgreSQL → MySQL 复制的开源工具,基于逻辑复制实现增量同步,适合有一定技术投入能力的团队自行部署 |
| ETL 工具(如 Kettle/DataX) | 适合数据类型转换规则复杂、需要定制清洗逻辑的场景,开发投入相对较大但可控性最强 |
选型建议优先考虑:是否需要在线增量同步、JSONB/数组等复杂类型的转换规则、团队对工具本身的运维能力。
PostgreSQL 迁移到 MySQL,本质上是从一个"类型系统严谨、功能丰富"的数据库迁移到一个"生态成熟、相对简化"的数据库,最大的挑战不在于语法翻译,而在于 JSONB、数组、扩展、继承表等 PostgreSQL 特色能力在 MySQL 里往往没有直接对应物,需要结合业务场景重新设计。建议把迁移拆解为"特性摸底—类型/结构重新设计—语法转换—双轨校验—灰度切换"五个阶段,尤其重视迁移前的特性摸底工作,这决定了后续工作量的量级。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/10201716/viewspace-3177167/ ,如需转载,请注明出处,否则将追究法律责任。