高效完成 Oracle 到 PostgreSQL 迁移:必须了解的数据库知识

4 阅读6分钟

嘿,朋友,如果你正在为 Oracle 到 PostgreSQL 的迁移挠头,或者你刚准备转行数据库这个圈子,想搞明白这两个“大腕儿”到底有啥不一样,那恭喜你,来对地方了。别怕,这事儿没那么玄乎。今天咱们就用最接地气的话,加上点实实在在的代码,把那些你必须知道的硬核知识聊透。你不仅能看懂,还能马上用起来。

 

先给你打打气:PostgreSQL 是个好东西,开源、灵活、社区活跃,很多大厂都在用。从 Oracle 迁过来,就像从住了很久的老房子搬进一个功能更现代的新家——需要收拾一下,但住进去之后是真舒服。咱们的目标不是“完美迁移”,而是“高效搞定”。废话不多说,上干货。

 

一、数据类型:别让“长得像”坑了你

 

Oracle 和 PG 的数据类型,很多名字一样,但脾气完全不同。最经典的坑就是空字符串和 NULL。在 Oracle 里,''(空串)会被当作 NULL 处理;但在 PG 里,空串就是空串,NULL 就是 NULL。这会导致你的查询逻辑悄悄变味儿。

 

举个栗子,Oracle 里你写:

SELECT * FROM users WHERE name = '';

实际查的是 name IS NULL。到了 PG,这条 SQL 就查不到任何数据,除非你把它改成:

SELECT * FROM users WHERE name IS NULL;

 

再比如,数字类型。Oracle 的 NUMBER 是个万能口袋,整数小数都能装。PG 里更讲究:整数用 INTEGER,小数用 NUMERIC 或 DECIMAL。迁移时,建议把 Oracle 的 NUMBER(p,s) 映射成 PG 的 NUMERIC(p,s),精度对得上。如果是 NUMBER 不带参数(比如自增主键),PG 里可以用 SERIAL 或 BIGSERIAL。

 

字符串也得小心:Oracle 的 VARCHAR2 到了 PG 就是 VARCHAR,但 PG 对字符长度限制更严格。你原来 VARCHAR2(10) 存 5 个中文没问题,PG 同样可以,不过 PG 是按字符数算的,别担心。

 

给你一个迁移小贴士:先跑一遍数据抽样,看看有没有空字符串和 NULL 混用的表。写个脚本批量替换 '''' 为 NULL?别急,先理清业务逻辑。

 

二、事务模型:PG 的“乐观”和 Oracle 的“回滚段”

 

Oracle 的并发控制靠的是回滚段(undo segments),读不阻塞写,写不阻塞读,很优雅。PG 呢,用的是 MVCC(多版本并发控制),本质上每条更新都会产生一个新版本的行,旧版本留在表里直到清理(VACUUM)。这对开发来说,事务行为基本一致,但性能特点不同。

 

在 PG 里,一个长事务会阻止 VACUUM 清理死元组,导致表膨胀。所以,别在 PG 里开一个事务然后去喝杯咖啡——提交或回滚要快。迁移后,你可能需要调整应用的自动提交设置,或者显式管理事务边界。

 

举个典型错误:原来 Oracle 的存储过程里可能习惯性不写 COMMIT,靠连接断开时自动提交。PG 里可没这好事,不 COMMIT 就真不落盘。所以迁移后,检查所有事务逻辑,确保每个 BEGIN 都有对应的 COMMIT 或 ROLLBACK。

 

三、性能调优:换个思路,别硬套 Oracle 的经验

 

在 Oracle 里你调优可能会盯着索引、统计信息、执行计划。PG 也看这些,但默认参数保守得像老干部。刚迁过去,头一件事就是调整 postgresql.conf。

 

几个必改的参数:

shared_buffers:设置为系统内存的 25% 左右,别太高。

effective_cache_size:给操作系统文件缓存留的预估,设成内存的 50%-75%。

work_mem:排序和哈希操作的内存,默认 4MB 太小,复杂查询可以调到 16MB-64MB,但注意这是每个操作消耗的,别撑爆。

 

还有,PG 的查询计划器默认认为索引扫描很贵,有时候需要你手动调低 random_page_cost(如果你用的是 SSD,改成 1.1 吧)。另外,PG 的 ANALYZE 要定期跑,否则统计信息过时,执行计划会跑偏。

 

给你一个实战命令:在迁移后的数据库上,先跑一遍 pg_stat_statements 扩展,看看哪些查询最耗资源。然后针对性加索引。PG 的索引类型比 Oracle 更丰富,比如 BRIN 索引对时间序列表非常省空间。

 

四、SQL 语法兼容性:那些让你“诶?”的细节

 

Oracle 的 PL/SQL 和 PG 的 PL/pgSQL 是远房亲戚,但语法细节能让你抓狂。先说几个高频差异:

 

字符串连接:Oracle 用 || 没问题,PG 也支持。但 Oracle 里 CONCAT 只能俩参数,PG 的 CONCAT 可以多个。放心,大部分能自动转。

 

分页查询:Oracle 用 ROWNUM 或 ROW_NUMBER(),PG 用 LIMIT 和 OFFSET,清晰多了。比如:

-- Oracle

SELECT * FROM (SELECT * FROM emp ORDER BY id) WHERE ROWNUM <= 10;

-- PG

SELECT * FROM emp ORDER BY id LIMIT 10;

 

日期运算:Oracle 里 SYSDATE - 1 表示昨天,PG 里用 CURRENT_DATE - INTERVAL '1 day'。更精确的写法:NOW() - '1 day'::INTERVAL。

 

存储过程/函数:Oracle 的 CREATE OR REPLACE FUNCTION 可以重载,PG 也支持。但 Oracle 的 OUT 参数和返回值混用比较复杂,PG 更推荐用 RETURNS TABLE 或者 SETOF。另外,PG 的函数默认是 SQL 语言,如果你想用过程式逻辑,要指定 LANGUAGE plpgsql。

 

举个函数迁移的例子,Oracle 里:

CREATE OR REPLACE FUNCTION get_user_name(p_id NUMBER) RETURN VARCHAR2 IS

v_name VARCHAR2(100);

BEGIN

SELECT name INTO v_name FROM users WHERE id = p_id;

RETURN v_name;

END;

/

PG 里写法:

CREATE OR REPLACE FUNCTION get_user_name(p_id INTEGER)

RETURNS VARCHAR AS $$

DECLARE

v_name VARCHAR(100);

BEGIN

SELECT name INTO v_name FROM users WHERE id = p_id;

RETURN v_name;

END;

 

 

注意符号和分号的位置。习惯了就好。

五、索引与约束:实现不同,目标相同Oracle里的索引默认是B−tree,PG也是。但PG的索引有很多“黑科技”:部分索引(WHERE条件)、表达式索引、GIN索引(全文检索)。迁移时,你可以趁机优化一波。比如Oracle里你建了一个普通索引在status列上,但查询只关心status= ′ACTIVE 的数据。PG里可以改成部分索引:CREATEINDEXidx a ctiveu sersONusers(last login)WHEREstatus= ′ACTIVE ′;体积更小,速度更快。约束方面,主键、外键、唯一约束、检查约束,两者基本一致。但有个细节:Oracle的非空约束是单独定义的,PG把它归为检查约束的一种。迁移工具通常能处理。还有一个大坑:Oracle支持基于函数的索引,比如UPPER(name)。PG也支持,语法几乎一样:CREATEINDEXidxupper nameONusers(UPPER(name));但是,PG要求这个函数是“不可变”的(IMMUTABLE)。如果用了某些可变函数,索引会失效。给你一个鼓励:别怕踩坑,每个坑都是你成为PG高手的机会。迁移不是搬家,是升级。最后,送你三句心里话;

第一,先做小范围试点迁移。选一张业务表,跑完数据、改完SQL、压测一下,心里就有底了。

第二,善用工具。比如ora2pg、pgloader能帮你搞定七八成的重复工作,但剩下两三成的精调必须自己上。

第三,加入PG社区。PostgreSQL的中文文档非常全,微信群和论坛里也有一堆热心大佬。你遇到的问题,大概率别人已经踩过并填平了。从Oracle到PostgreSQL,不是放弃老朋友,而是拥抱更自由、更现代的开源世界。你完全搞得定。现在,打开你的测试环境,写几行PG代码,试试今天聊的这些知识点。如果卡住了,记得你不是一个人。加油!

以上观点仅供参考,有什么技术问题可以私信小编。