八月 12, 2026
摘要:在本教程中,我们会介绍将 Oracle 迁移至 PostgreSQL 时,数据库底层设计差异带来的影响。
目录
核心背景与概述

- 分享团队:汉莎航空集团(Lufthansa Group)收益核算部门开发团队。
- 项目背景:团队在对 PostgreSQL 几乎零基础的情况下,将其负责的核心票务系统从 Oracle 成功迁移至 PostgreSQL。
- 核心痛点:数据库迁移绝不仅仅是 SQL 语法的替换,更关键的是底层数据库设计与机制的差异。不理解这些差异,会导致严重的性能问题与数据逻辑错误。
语法与函数的差异及替换方案 (Syntax & Functions)
在迁移初期,团队处理了许多基础的语法转换,并发现了隐藏的逻辑陷阱:
1. 空值函数替换 (NVL vs COALESCE)
- 差异:Oracle 常用
NVL处理空值;PostgreSQL 不支持该函数。 - 方案:直接使用正则表达式,将
NVL全局替换为 PostgreSQL 的标准函数COALESCE。
2. 类型转换 (隐式 vs 显式)
- 差异:Oracle 具备极强的隐式类型转换能力(例如字符串直接加减);而 PostgreSQL 要求严格的显式类型转换。
- 方案:需要在代码中明确添加 Type Cast(类型转换)。团队还发现,对于特定的需求(例如在整数末尾补两个零),与其使用“转字符串拼接再转整数”的复杂操作,不如直接“乘以100”来得高效简洁。
3. 条件匹配陷阱 (DECODE vs CASE)
- 差异:Oracle 的
DECODE函数允许将NULL与NULL进行等值匹配;而在 PostgreSQL 中替换为CASE语句时,标准的NULL = NULL结果为false。 - 初级方案:使用
CASE WHEN ... IS NULL或IS NOT DISTINCT FROM替代,确立正确的空值判断逻辑。 - 性能危机与最终方案:在进行表连接(JOIN)时,由于 PostgreSQL 查询规划器(Query Planner)目前无法为
IS NOT DISTINCT FROM充分利用索引,导致性能极差。为了追求极致性能,团队采用了一种变通方案:将比较的 NULL 值转换为特定的占位字符串(如||NULL||)。这样就可以使用标准的等号(=)进行比较并建立普通索引,大幅提升了查询效率。
数值计算与精度差异
两种数据库在处理数值(尤其是除法和高精度计算)时表现截然不同,这在涉及财务结算的系统中尤为致命。
整数除法差异:
- Oracle 中执行
1 / 3会返回0.333...。 - PostgreSQL 中严格区分整数,
1 / 3的整数相除会向下取整,返回0。
精度管理机制对比:
- Oracle:只有一个精确的数值类型(
NUMBER),在进行不精确计算时,会自动填满其支持的最大精度(约 39-40 位有效数字)。 - PostgreSQL:最大支持 1000 位精度。出于性能考量,无法每次都填满。对于计算结果,它高度依赖于操作数的声明精度(Scale)。如果不显式指定,可能导致财务数据精度丢失。
解决方案:为避免在代码各处手动添加易遗漏的类型转换,团队直接在表结构定义阶段出击,将所有相关数值字段统一修改为高精度类型 NUMERIC(52, 40),从根本上杜绝了计算错误。
核心挑战:重构 UPDATE 逻辑 (MVCC 机制差异)

这是整个迁移过程中最核心的性能与架构挑战。
业务场景:每月需对一张约 2 亿条记录的大表进行滚动处理。客户提交的数据往往不完整,需将约 1 亿条未完成的记录 UPDATE(更新)至下一个月。
Oracle 的表现 (In-place Update):
- Oracle 支持“原地更新”,依靠 Undo Log 管理事务,执行 1 亿条数据的更新仅需约 20 分钟。
PostgreSQL 的表现 (MVCC 多版本并发控制):
- PostgreSQL 不支持原地更新。每次 UPDATE 实际上是插入一条全新的记录(Tuple),并将旧记录标记为废弃(
xmax)。 - 对大表进行 50% 规模的更新,会导致表体积瞬间膨胀一倍(表膨胀/Bloat)。团队在测试中运行了 24 小时都未能完成该 UPDATE 操作。
常规维护手段的局限性:
- 使用
VACUUM或重排表空间会产生极大的系统负载和锁表风险。 - 因业务有严格的全局唯一性约束(Global Uniqueness Constraints),无法轻易通过表分区(Partitioning)来绕过问题。
最终破局策略:调整业务逻辑
- 结论:在 PostgreSQL 中,必须尽量避免大规模的批量 UPDATE。
- 改造:团队摒弃了“每月搬运海量未完成数据”的做法。改为默认保留状态为空,只有当数据确实“已完成”时,才去 UPDATE 那一小部分完成的记录。这一业务逻辑的转换不仅秒解了性能危机,也彻底避免了表膨胀。
数据库迁移的 6 步标准指南 (团队总结)

根据本次实战,团队总结了异构数据库迁移的标准步骤:
- 先跑起来:尽早让系统在目标数据库上运行起来,这是发现潜在问题的最快途径。
- 排查基础替换错误:重点检查基础语法和函数替换(如正则替换)是否产生了逻辑偏差。
- 设定性能目标:明确是需要对齐老系统的性能,还是建立新标准。
- 常规性能调优:通过添加索引、调整配置等常规手段优化查询。
- 解决系统性缺陷:如遇底层机制导致的性能断崖(例如前文的 UPDATE 问题),不要死磕 SQL,必须果断调整业务逻辑或内部处理流程。
- 回顾与二次优化:由于业务逻辑发生了改变,需要回退一步,重新评估数据结构和性能,直至达标。
其他重要启示
- 严格是件好事:Oracle 对很多不规范的 SQL 写法(Bad Practices)包容度极高。PostgreSQL 相对严格,这种“严格”使得旧代码中的隐患暴露无遗,客观上倒逼团队修复了大量历史技术债,提升了代码质量。
- 迁移工具选择:团队并没有过度依赖自动化转换工具(如 ora2pg),而是选择手动迁移 Schema、使用自定义脚本进行数据迁移,以此更好地处理字段重命名等定制化需求。
参考
Oracle to PostgreSQL beyond the Syntax: When DBMS Design Differences Matter