Oracle 迁移到 PostgreSQL 完整指南(去O)
从 Oracle 迁移到 PostgreSQL 的完整指南——兼容差异地图、三条迁移路线、评估与 CDC 工具链、五阶段切换流程与回滚预案。
Oracle 迁移到 PostgreSQL,卡住项目的通常不是数据量,而是数据周围积累的一切:PL/SQL 代码、专有 SQL、运维习惯和组织惯性。本文梳理真实存在的差异、对比三条可行路线、列出工具链,并给出带回滚预案的五阶段流程。
四类迁移阻力
各类评估反复发现同样的四个摩擦来源。先把它们说清楚,项目才不会对工作量自欺欺人。
- 专有语法与 PL/SQL。
CONNECT BY、(+)外连接、ROWNUM、NVL、DECODE、DUAL,以及嵌在应用代码里的 PL/SQL 块。单看每个都不大,问题在总量。 - 存储过程与包。 放在数据库里而不是应用里的业务逻辑。Oracle 包把过程、函数和包级状态组织成一个编译单元——社区版 PostgreSQL 没有直接对应物,因此人工改写集中在这里。
- 生态绑定。 只存在于 Oracle 世界的工具与特性:AWR 报告、Enterprise Manager、Data Guard、RMAN 惯例、Oracle 专属监控,以及按 Oracle 行为配置的驱动和框架。即使 SQL 能顺利移植,每个集成点仍是单独的迁移任务。
- 组织惯性。 团队技能、变更冻结制度、厂商合同,以及对风险的感知。它对工期的影响超过任何技术因素,而且没有工具能解决它。
Oracle ↔ PostgreSQL 兼容差异地图
下面列的是真正会弄坏迁移的差异,全部为两个系统的文档化行为。
数据类型
| Oracle | PostgreSQL | 说明 |
|---|---|---|
NUMBER(p,s) | numeric(p,s) | 裸 NUMBER 映射为 numeric;只有核实过取值范围后才改用 integer/bigint |
VARCHAR2(n) | varchar(n) | 注意 Oracle 的 BYTE 与 CHAR 长度语义,对照目标库编码 |
CHAR(n) | char(n) | 两边都填充,但空字符串行为不同(见下) |
DATE | timestamp | Oracle DATE 含时间分量,PG 的 date 不含 |
CLOB | text | PG 无需指定长度上限 |
BLOB | bytea | |
ROWID | 无对应物 | ctid 是物理位置,UPDATE 后会变,绝不能当行标识用 |
SYSDATE | CURRENT_TIMESTAMP / now() |
SQL 方言
| Oracle | PostgreSQL |
|---|---|
WHERE ROWNUM <= 10 | LIMIT 10(补上显式 ORDER BY;Oracle 的 ROWNUM 过滤发生在排序之前) |
SELECT ... FROM DUAL | 去掉 FROM DUAL——SELECT 1; 本身合法 |
NVL(a, b) | COALESCE(a, b) |
DECODE(x, 1, 'a', 'b') | CASE WHEN x = 1 THEN 'a' ELSE 'b' END |
a.col = b.col(+) | LEFT JOIN |
CONNECT BY PRIOR id = parent_id | WITH RECURSIVE 递归 CTE |
seq.NEXTVAL / seq.CURRVAL | nextval('seq') / currval('seq') |
空字符串 vs NULL
Oracle 把空字符串当作 NULL;PostgreSQL 区分 '' 和 NULL。这是最危险的语义差异,因为它会静默改变查询结果、唯一约束行为和拼接结果('a' || NULL 在 Oracle 里是 'a',在 PostgreSQL 里是 NULL)。所有写入或比较字符串的应用路径都要逐一审查,而不只是 DDL。
序列与 identity 列
两个数据库都有序列,但调用方式不同,而且导入数据不会推进序列计数器:
-- Oracle
INSERT INTO orders (id) VALUES (orders_seq.NEXTVAL);
-- PostgreSQL
INSERT INTO orders (id) VALUES (nextval('orders_seq'));
-- 或者用标准 identity 列替代显式序列
CREATE TABLE orders (id bigint GENERATED BY DEFAULT AS IDENTITY, ...);
-- 任何批量数据导入之后,把序列重置到已导入最大值之上
SELECT setval('orders_seq', (SELECT max(id) FROM orders));切换后忘记 setval,会在几天或几周后以主键冲突的形式爆发。把它写进切换操作手册(runbook)。
同义词、包与 schema 组织
PostgreSQL 没有同义词对象。标准替代做法是用 search_path 配置解决名称解析,或者用视图暴露其他 schema 的对象——Ora2Pg 正是把同义词导出为视图。
Oracle 的包在社区版 PG 里同样没有对应物。常见映射是一个包对应一个 schema,包里的过程和函数变成 schema 下的函数。包级变量没有干净的等价物,通常要移到应用状态或会话级配置里——这也是包重度系统成为迁移中昂贵部分的原因之一。
PL/SQL → PL/pgSQL:主要改写点
PL/pgSQL 在结构上与 PL/SQL 相似,相当部分过程化代码可以机械移植。反复出现的改写点:
- 没有
PRAGMA指令;异常处理用BEGIN ... EXCEPTION WHEN ... THEN,WHEN OTHERS要格外小心,因为 PG 的错误处理会在子事务内中止该语句已做的工作。 - 没有
DBMS_OUTPUT.PUT_LINE——用RAISE NOTICE。 - 没有包状态(见上);没有自治事务(见下)。
%TYPE和%ROWTYPE在 PL/pgSQL 中存在,可直接移植。- PostgreSQL 的
CREATE OR REPLACE不能修改函数返回类型;某些重新部署流程需要先DROP FUNCTION。
自治事务
Oracle 的 PRAGMA AUTONOMOUS_TRANSACTION 允许过程独立于调用方事务提交——常用于即使主事务回滚也必须保留的审计日志。PostgreSQL 没有自治事务。各种变通方案(用 dblink 连自己、background worker、把日志移出数据库)都会改变语义。依赖该特性的代码必须重新设计,而不是翻译。
Hint
Oracle 的 /*+ ... */ 优化器 hint 对 PostgreSQL 来说只是注释——被静默忽略。PostgreSQL 通过统计信息、配置参数和查询结构来引导优化器;pg_hint_plan 扩展为确实需要 hint 式控制的团队提供了选择,但更好的第一步是用 ANALYZE 修好统计信息并审视查询本身。凡是曾在 Oracle 里起关键作用的 hint,迁移后都需要显式的执行计划复核。
兼容层减少改写量,不消灭改写工作
下面每条路线都仍然需要针对真实应用负载做测试。兼容模式改变的是需要改写的 SQL 数量,并不改变流程一节中验证清单的必要性。
三条路线
路线一:直迁社区版 PostgreSQL
改写 schema、把 PL/SQL 移植为 PL/pgSQL、跑在标准 PostgreSQL 上。前期投入最高,长期依赖最低:最终落在主线项目上,拥有完整扩展生态,没有许可费用,也没有需要跨版本跟踪的兼容层。适合存储代码量适中的系统,或者希望彻底移除 Oracle 依赖而不是模拟它的组织。
路线二:商业兼容层——EDB Postgres Advanced Server
EDB Postgres Advanced Server 是带 Oracle 兼容模式的商业 PostgreSQL 发行版,提供 Oracle 兼容的数据类型、关键字、内置函数、Oracle 风格 catalog 视图和扩展的 MERGE 兼容性,外加 EDB 自有工具与支持。它能显著降低 PL/SQL 重度系统的改写量,代价是商业许可和对 EDB 发布节奏的依赖。适合改写在经济上不可行的大型套装软件系统。
路线三:开源兼容分支——IvorySQL
IvorySQL(GitHub,Apache 2.0)是基于 PostgreSQL 的分支,跟随上游 PostgreSQL 版本演进,在其上叠加 Oracle 兼容性。以下说法已于 2026-08 对照项目文档与发布记录核实:
- 双模式初始化:
initdb -m pg产出的集群行为与原生 PostgreSQL 一致;initdb -m oracle(默认)启用 Oracle 兼容模式。ivorysql.compatible_modeGUC 参数可在运行时切换两种模式。 - 双解析器 / 双端口:5432 端口提供原生 PostgreSQL 兼容;Oracle 模式默认使用 1521 端口并配备独立的 Oracle 解析器,两套语法互不干扰。
- PL/iSQL:接受 Oracle PL/SQL 语法的过程语言,支持 Oracle 风格的包;
ivorysql_ora扩展提供 Oracle 内置函数。 - IvorySQL 5.0 基于 PostgreSQL 18。
这条路线适合希望获得 Oracle 语法兼容但不引入商业许可的团队。代价是厂商生态较小,且需要同时测试两种模式——Oracle 模式的行为与上游 PostgreSQL 并不相同,而这个差异正是迁移测试必须覆盖的部分。IvorySQL 在整个兼容版图中的位置见 PostgreSQL 血缘、分支与兼容数据库。
工具链
Ora2Pg——评估与导出
Ora2Pg 是标准的开源起点。两项能力最关键:
- 评估报告:以
SHOW_REPORT加--estimate_cost运行,扫描 Oracle 数据库并生成报告(文本、HTML、CSV 或 JSON),列出每种对象类型、转换状态和按人日估算的迁移成本。这是在动任何代码之前为项目划定范围的事实依据。 - 模式与数据导出:按对象类型导出(表、视图、序列、函数、过程、包、触发器、分区、同义词),附带 PL/SQL 到 PL/pgSQL 的自动转换,转换结果仍需人工复核。数据以
COPY或INSERT导出,支持并行抽取和直接导入 PostgreSQL。
ora_migrator——FDW 方式
ora_migrator(CYBERTEC)是基于 oracle_fdw 与 db_migrator 的 PostgreSQL 扩展。它不导出文件,而是对着 Oracle 库建外部表,通过 SQL 函数完成模式与数据迁移,其中包括用于评估的 migration_cost_estimate 视图,以及在迁移前标记问题行(如字符串列中的零字节或编码非法值)的数据校验函数。它还提供从 Oracle 到 PostgreSQL 的触发器式增量追平复制,可用于近零停机切换。
AWS DMS Schema Conversion
DMS Schema Conversion 是 AWS 的托管功能(基于 AWS SCT 引擎),把 Oracle 模式转换为 Aurora PostgreSQL 或 RDS for PostgreSQL。它生成转换评估报告,标明哪些对象能自动转换、哪些需要人工处理,转换结果可以直接应用到目标库或导出为 SQL。它只转换模式——数据迁移是 AWS DMS 的独立任务。目标是 AWS 托管 PostgreSQL 时适用;自托管部署则关系不大。
用 CDC 实现近零停机
批量导出导入意味着与数据量成正比的停机时间。CDC 把其中的大部分消掉:先装载初始快照,再持续流式追平变更,直到切换。
- Debezium Oracle connector:通过 LogMiner 适配器读取 Oracle redo 日志(另有 XStream 与 OpenLogReplicator 适配器),先做初始快照,再把行级变更流入 Kafka,由 sink 消费写入 PostgreSQL。
- AWS DMS ongoing replication(CDC):目标在 AWS 上时的托管等价方案。
- ora_migrator 复制(见上):基于触发器、只需扩展、不需要额外基础设施的选项。
无论选哪条 CDC 路径,切换收尾都有两个容易遗漏的步骤:确认变更流延迟已追平到零,以及对照已迁移数据用 setval 重置所有序列(见上文序列一节)。复制相关的运维细节见 复制与大版本升级。
五阶段迁移流程
- 评估。 对生产 schema 运行 Ora2Pg 评估报告(或 ora_migrator 的成本估算、DMS 的评估)。输出决定路线选择:对象数量、转换覆盖率和人日估算告诉你直迁、兼容层还是托管转换更现实。同时盘点应用侧的 Oracle 耦合(驱动设置、代码中的 SQL、报表工具)——数据库报告看不到这些。
- 试点。 选一个边界清晰的 schema 或服务,端到端迁移并在类生产条件下运行。试点用现实校准评估数字,并在小规模暴露语义陷阱——空字符串、日期、序列、hint。
- 双跑 / 灰度。 CDC 运行起来后,把一部分真实流量(或完整的影子负载)导向 PostgreSQL,与 Oracle 对比结果:查询输出、事务结果、真实负载下的性能。试点漏掉的东西在这里现形。
- 切换。 CDC 延迟追平到零,停止 Oracle 写入,重置序列,跑最终验证,然后把应用指向 PostgreSQL。Oracle 实例保持只读并完整保留——它就是你的回滚目标。切换本身的每一步 DDL 都按 安全迁移 的生产规范执行。
- 回滚预案与验证清单。 事先定义回滚触发条件和数据流向(如果切换后 PostgreSQL 上已经产生写入,回滚需要反向复制或数据调和程序——这在切换前定好,而不是在事故中现想)。最低验证清单:
- 逐表行数(Ora2Pg 的
TEST/TEST_COUNT动作可自动做差异对比)。 - 约束:主键、外键、唯一、check、NOT NULL——两侧的数量与启用状态。
- 索引:数量、定义与有效性。
- 序列:
setval已执行,nextval返回值高于已迁移最大值。 - 关键事务:一组固定的核心业务查询与过程,在两个系统上运行并对比结果。
- 对类型敏感列做数据抽查:日期、带精度的数值、空字符串、CLOB/BLOB 内容。
- 逐表行数(Ora2Pg 的
组织层面
工具决定转换速度,工程纪律决定迁移成败。为改写和验证工作留出预算,让回滚路径真实可用,把"去O"当作一个有阶段、有负责人、有退出标准的工程项目,而不是一次工具采购。
Last updated on