4、兼容性评估:MySQL/PG 协议兼容性、SQL 方言差异、存储过程与触发器迁移
好,咱们进入第四讲。这一讲我打算聊聊兼容性评估。说实话,这是云原生数据库迁移中最容易翻车的地方。你想想看,业务跑得好好的,一迁移过去,SQL 报错、存储过程跑不起来、触发器失效……那画面太美我不敢看。
我个人习惯,在做任何迁移之前,先花一周时间做兼容性摸底。别急着切流量,先把坑踩一遍,心里才有底。
4.1 MySQL/PG 协议兼容性
先说协议层。云原生数据库为了兼容生态,通常会支持 MySQL 或 PostgreSQL 的通信协议。但「支持」和「完全兼容」是两码事。
MySQL 协议兼容性
我遇到过不少号称兼容 MySQL 的数据库,比如 TiDB、OceanBase、PolarDB-X。它们都能听懂 MySQL 的握手协议、查询协议、预处理语句。但细节上会有差异:
- 字符集:有些数据库只支持 utf8mb4,不支持 latin1。你原来的表如果用了 latin1,迁移后可能乱码。
- 认证插件:MySQL 8.0 默认用 caching_sha2_password,老应用可能只支持 mysql_native_password。我踩过这个坑,迁移后应用连不上库,排查了半天。
- 系统变量:像 sql_mode、time_zone 这些,不同数据库的默认值可能不同。建议迁移前统一设置。
核心建议:不要只看「兼容 MySQL 协议」这几个字。拿你的应用实际连一下,跑一遍全链路测试。协议兼容只是入门,真正的坑在 SQL 层面。
PostgreSQL 协议兼容性
PG 协议这边,像 CockroachDB、YugabyteDB、Greenplum 都支持。但 PG 的生态更复杂:
- 扩展机制:PG 有丰富的扩展(如 PostGIS、pg_stat_statements),但云原生数据库不一定支持所有扩展。我见过有人迁移后才发现 PostGIS 用不了,只能回退。
- 数据类型:PG 的数组、JSONB、枚举类型,有些数据库支持得不完整。比如 JSONB 的索引方式可能不同。
- 事务隔离级别:PG 默认是读已提交,但有些云原生数据库默认是可重复读。这会导致业务逻辑出问题。
我的经验:迁移前先列一个清单,把你应用中用到的所有 PG 特性都写下来。然后对照目标数据库的文档,一个一个打勾。别偷懒,这一步省不了。
4.2 SQL 方言差异
SQL 方言差异,说白了就是同一个 SQL 写法,在不同数据库里可能含义不同,甚至直接报错。这是迁移中最头疼的部分。
常见差异点
| 差异项 | MySQL 写法 | PG 写法 | 云原生数据库常见行为 |
|---|---|---|---|
| 分页 | LIMIT 10 OFFSET 20 |
LIMIT 10 OFFSET 20 |
大部分兼容,但某些数据库不支持 OFFSET 单独使用 |
| 字符串拼接 | CONCAT(a, b) 或 a || b |
a || b |
有些数据库只支持 CONCAT,不支持 || |
| 日期函数 | NOW(), CURDATE() |
CURRENT_TIMESTAMP, CURRENT_DATE |
函数名不同,需要手动替换 |
| 空值排序 | NULL 默认排在最前 | NULL 默认排在最后 | 需要显式指定 NULLS FIRST 或 NULLS LAST |
| 自增列 | AUTO_INCREMENT |
SERIAL 或 IDENTITY |
语法不同,DDL 需要改写 |
如何应对?
我建议分三步走:
- 静态扫描:用工具(如 SQLFluff、Sqitch)扫描所有 SQL 文件,找出不兼容的语法。
- 动态测试:把业务 SQL 在目标数据库上跑一遍,看有没有报错。我习惯写一个自动化脚本,逐条执行并记录结果。
- 人工审查:对于复杂的 SQL(比如多层嵌套子查询、窗口函数),人工过一遍最稳妥。机器有时候会漏掉逻辑差异。
注意:有些差异不会报错,但结果不同。比如 NULL 排序、字符串比较的 collation 规则。这种「静默错误」最危险,一定要用数据对比来验证。
4.3 存储过程与触发器迁移
存储过程和触发器,嗯,这里要注意。它们是迁移中的「硬骨头」。为什么?因为不同数据库的存储过程语言完全不同。
存储过程迁移
MySQL 用 SQL/PSM(类似 PL/SQL),PG 用 PL/pgSQL。语法差异很大:
- 变量声明:MySQL 用
DECLARE,PG 也用DECLARE,但位置和语法有细微差别。 - 游标:MySQL 的游标语法和 PG 不同,需要改写。
- 异常处理:MySQL 用
DECLARE ... HANDLER,PG 用EXCEPTION块。逻辑要重新组织。 - 动态 SQL:MySQL 用
PREPARE和EXECUTE,PG 用EXECUTE ... USING。
我遇到过最头疼的情况:一个存储过程有 500 行,里面用了大量 MySQL 特有的函数和游标。迁移时几乎重写了一遍。所以我的建议是:
能不用存储过程就不用。把业务逻辑放到应用层,数据库只做数据存储和查询。这样迁移成本最低。如果实在要用,那就做好重写的心理准备。
触发器迁移
触发器相对简单一些,但也要注意:
- 触发时机:MySQL 支持
BEFORE和AFTER,PG 还支持INSTEAD OF(用于视图)。 - 触发事件:MySQL 的
INSERT、UPDATE、DELETE和 PG 一致,但 PG 还支持TRUNCATE。 - 新旧行引用:MySQL 用
NEW和OLD,PG 也用NEW和OLD,但语法略有不同。
我曾经迁移过一个系统,里面有 20 多个触发器,用来做数据审计。迁移到目标数据库后,发现触发器执行顺序和原来不同,导致审计数据错乱。排查了两天才找到原因。
避坑指南:迁移触发器时,一定要写单元测试。每个触发器单独测试,验证输入输出是否一致。别偷懒,否则上线后出问题,你连觉都睡不好。
4.4 兼容性评估工具与流程
最后,我分享一下我常用的兼容性评估流程:
- 导出 DDL:从源数据库导出所有表结构、索引、约束、存储过程、触发器。
- 自动转换:用工具(如 AWS DMS Schema Conversion、Oracle SQL Developer)尝试自动转换。
- 人工修正:工具转换不了的,手动改写。我一般会列一个「待办清单」,逐条处理。
- 功能测试:在目标数据库上创建对象,跑一遍功能测试用例。
- 性能测试:同样的 SQL,在源库和目标库上对比执行计划,看有没有性能退化。
- 数据对比:迁移一部分数据,用工具(如 pt-table-checksum)对比源库和目标库的数据一致性。
嗯,这套流程我用了很多年,基本没出过大问题。你想想看,迁移就像搬家,提前把东西分类打包好,到了新家才能快速归位。兼容性评估就是这个「打包」的过程。
下一讲,我们会聊数据迁移策略。到时候我会讲讲全量迁移、增量同步、还有双写方案。咱们到时候见。