2026 年初,我们开始把一个 Go 服务的主数据库从 MySQL 迁往 PostgreSQL。

最早的信号不是 SQL 报错,而是全局变量叫 MysqlDb。业务代码拿到的不是“业务数据库”,而是一个已经带有实现承诺的对象。

迁移清单

我按下面几类逐步扫描。

连接层

  • 驱动、DSN 和连接参数;
  • 健康检查与初始化顺序;
  • 测试环境是否仍默认启动 MySQL;
  • 全局连接命名是否泄露具体实现。

因此 MysqlDb 被改为更中性的 WebcenterDb。重命名没有解决兼容问题,但它划清了以后应该遵守的边界。

模型层

MySQL 与 PostgreSQL 在自增、布尔值、时间、JSON、数组和默认值上都有差异。GORM 可以生成 SQL,却不能替业务判断类型是否仍然符合原有语义。

尤其要关注零值更新。某些代码依赖 Updates(struct) 忽略零值的行为,迁移过程中顺手修改模型,很容易让原本能写入的 false0 和空字符串再次被忽略。

查询层

最危险的是散落的原生 SQL:

  • MySQL 专用函数;
  • 模糊查询与大小写行为;
  • 字符串和时间格式化;
  • 表名、字段名大小写;
  • 数组条件;
  • 分页和排序中的隐式假设。

我不追求把所有 SQL 写成“同时兼容两个数据库”。兼容层过厚会使查询更难读。更实际的方式是明确目标数据库,并把少量方言差异收敛在 repository 或 helper 中。

数据与回滚

应用能启动只是开始。还要对比表数量、行数、关键关联、时间值和典型查询结果;大表迁移要有进度;上线窗口要说明旧系统新增的数据如何处理;回滚时新数据又该写回哪里。

AI 在迁移里适合做什么

AI 很适合扫描 MysqlDb、MySQL 函数和可疑原生 SQL,生成一份初始差异清单,也适合补充测试样例。

但它无法知道某个奇怪 SQL 是历史 Bug,还是客户一直依赖的行为。迁移中最需要人的部分,恰恰是决定哪些行为必须保持,哪些应该趁机修正。

数据库迁移是一场很诚实的架构检查。平时以为已经被 ORM 隔离的细节,会在此时全部重新出现。