Oracle 11g到19c升级实战:路径规划、补丁选择与高频故障排查
2026/9/18 12:36:15 网站建设 项目流程

我先把这个项目的背景说清楚。我们刚给一套跑了八年的Oracle 11.2.0.4库完成了19c升级,用的是目前较新的19.25.0.0.241015版本。整个升级过程下来,最大的感受是:升级本身没那么吓人,真正让人反复折腾的,是升级路径怎么规划、补丁版本怎么确认,以及升级之后冒出来的监听起不来、ORA-28547、DG主备出现gap这类问题。这篇我不讲基础安装教程,只讲从老版本升到19c怎么落地,以及我这些年在群里、论坛里收到频率最高的几个顶级Q&A,希望能帮你绕开几个大坑。

1. 升级路径怎么选:从11g/12c到19c的路线图

1.1 为什么是19c:版本定位和生命周期

很多客户问我第一句话就是:现在19c还能用几年?为什么一定要升19c?这里面有个特别容易被忽略的事实:Oracle 19c的内部版本号就是12.2.0.3,它属于12c这个家族的最后一个大版本更新,也是Oracle官方定义的长期支持版本。跟它同时期的21c、23c都是“创新版本”,生命周期短,更适合尝鲜,而19c的扩展支持可以一路走到2027年前后,这意味着在可预见的未来,19c就是企业非云环境下性价比最高的稳定版本。

另一个关键点:从19c开始,非CDB架构虽然在升级时可以保留,但已经被官方明确标记为“不建议继续使用”,后面更新的版本对非CDB的支持越来越少。所以如果你现在还在跑11g时代的普通实例,升到19c正好是一个把非CDB改造成CDB/PDB结构的机会。虽然“改架构”听起来像额外工作量,但长远看,这一步越早做越省事。

1.2 不同老版本对应的升级路线

升级路径不是想当然的直接覆盖,Oracle对不同版本的源库支持力度完全不同。我整理了一张实战中最高频的路径表:

源库版本是否支持直升19c说明
10.2.0.x 及更早必须先升到11.2.0.4或12.2,再升19c
11.2.0.3需要先补丁到11.2.0.4
11.2.0.4最常见的升级源库,路径最成熟
12.1.0.2可直接升,但要注意compatible参数
12.2.0.1同属12.2系列,升级最顺滑
18c与19c血缘最近,基本像打补丁

这里要特别提醒:如果你的库还在10g甚至更老,不要想着一步到位。道理很简单,跨越多个大版本的升级会导致数据字典里的内部格式变化太大,Oracle官方压根没有对极老版本做直升认证,硬升大概率会在字典升级阶段报一堆奇奇怪怪的ORA错误。稳妥的做法是先拿到11.2.0.4这个“中转站”,再走主流直升路径。

1.3 版本号19.25.0.0.241015到底表示什么

很多人看到“19.25.0.0.241015”这个版本号就懵了,我简单拆一下:

  • 19代表数据库主版本,也就是19c;
  • 25代表这是第25个季度的Release Update(RU补丁版本);
  • 241015代表补丁发布于2024年10月15日。

Oracle 19c从19.3基础版本开始,每个季度都会发布一个RU补丁包,把安全更新、bug修复、性能优化打包在一起。所以你在19c基础版本上打补丁到19.25,实际效果就是“2024年10月份前所有的累积修复都包含进去了”。我的建议是:新环境直接部署最新RU补丁,老环境升完19c后,第一时间把补丁也升到当前季度附近,避免拿个2019年的裸版本上线,那样反而会踩很多已经修掉的bug。

2. 升级前准备与升级实操

2.1 升级前必须完成的环境检查

升级最忌讳一上来就改环境,先把地基打牢。我每次做升级前都会过一遍下面几项,任何一项不过关都不动数据库:

  • 磁盘空间:新ORACLE_HOME需要额外空间,升级过程中字典表会膨胀,undo表空间也要预留充足。我曾经见过升级到一半日志把磁盘塞满的案例,直接导致实例崩溃,只能靠备份恢复。
  • RMAN全备:必须在升级前做一次完整备份,并确认备份文件能被Restore。有条件的话把备份拷贝到异机,别跟数据库放同一块盘。
  • 失效对象检查:升级前先跑一遍select owner, object_type, count(*) from dba_objects where status = 'INVALID' group by owner, object_type;,如果失效对象特别多,升级过程会更长,最好提前记录baseline。
  • 兼容性参数:确认当前数据库的compatible值,不要把已经设置得很高的compatible降下来,否则很多新特性会不可用。
  • 应用侧兼容性:JDBC驱动、中间件版本、OGG版本都要和19c匹配。尤其注意老旧的ojdbc6或11g的OCI客户端,强烈建议升级到ojdbc8及以上。

2.2 手工升级核心步骤:catctl并行方式

19c开始,官方主推的命令行升级方式是catctl.pl并行脚本,相比老版本的catupgrd.sql串行升级,速度能快不少。下面是我实测过的可靠流程:

第一步:用preupgrade.jar做全面体检

新19c软件安装完成后,不要着急动库,先跑预检查:

cd $ORACLE_HOME/rdbms/admin $ORACLE_HOME/jdk/bin/java -jar preupgrade.jar TERMINAL

这个工具会生成一个风险报告,并输出preupgrade_fixup.sqlpreupgrade_postupgradefixup.sql两个脚本。前者要在旧库上执行,处理已知风险;后者升级后再跑。这一步千万别跳过,它基本就是Oracle官方给的“考前划重点”。

第二步:切换到新ORACLE_HOME并启动到upgrade模式

export ORACLE_HOME=/u01/app/oracle/product/19.3/dbhome_1 export PATH=$ORACLE_HOME/bin:$PATH export ORACLE_SID=ORCL sqlplus / as sysdba SQL> startup upgrade;

startup upgrade模式允许系统在执行字典升级时跳过不必要的检查和日志级别降低,这个模式只有升级期间使用,日常不要这么启动。

第三步:用catctl并行跑核心升级脚本

cd $ORACLE_HOME/rdbms/admin $ORACLE_HOME/perl/bin/perl catctl.pl -n 8 catupgrd.sql

这里的-n 8是并行度。并行度不是越大越好,经验值一般是CPU核数的四分之一到八分之一。因为每个并行进程还会继续派生子进程,并行度太高会把CPU和IO拖垮,反而更慢。如果你的库是CDB,多个PDB的字典升级也会自动排进这个并行流程,所以大库建议把并行度稍微调低,宁可慢一点也要稳。

第四步:重启实例并编译无效对象

SQL> shutdown immediate; SQL> startup; SQL> @?/rdbms/admin/utlrp.sql

utlrp.sql会重新编译升级期间失效的对象,这是19c升级后必跑的动作,一般几十分钟内能完成,具体取决于无效对象数量。

第五步:升级后验证

select * from v$version; select comp_id, comp_name, version, status from dba_registry; select patch_id, action, status from dba_registry_sqlpatch;

这三条SQL分别确认数据库版本、组件版本和补丁状态。记得同时看一眼alert_ORCL.log,关注是否有ORA-错误堆栈,尤其是升级过程中出现的ORA-600类错误。

2.3 DBUA图形升级为什么我不推荐

Oracle也提供DBUA图形向导升级,点击下一步就能完成大部分配置。对小库来说DBUA确实省事,但我个人在生产环境升级中不推荐它,原因有三:一是DBUA的并行度和内部调度是黑盒,出了问题不好控制;二是它在交互过程中容易因为环境变量、X11转发等问题中途卡住;三是自动化运维时,命令行方式更容易沉淀成脚本复用。当然,如果你第一次升级、库不大,DBUA完全可以用,只要提前知道它会在后台帮你自动跑preupgrade和catctl,结论是一样的。

3. 升级后高频Q&A:监听、连接、DG这些坑

3.1 监听服务无法启动

这大概是19c升级后出现频率最高的故障。典型症状是lsnrctl start启动后立刻退出,报错常见于TNS-12541、TNS-01189,或者干脆日志里什么都没写。

排查顺序我建议固定下来:

  1. $ORACLE_HOME/network/admin/listener.ora,重点检查括号是否匹配、HOST那一项是否用了无法解析的主机名;
  2. 在服务器上先ping 主机名,再cat /etc/hosts确认解析没问题;
  3. 检查1521端口是否被别的进程占用,netstat -an | grep 1521
  4. 在listener.ora里临时把HOST改成localhost或实际IP,排除主机名配置问题。

如果是Windows服务方式启动,还要额外检查服务账户是否有权限、服务是否被残留。19c最常踩的一个坑是老版本卸载不干净,注册表里残存的旧监听服务还在占用端口,这时哪怕配置没问题也起不来。解决办法是删除旧服务后重启Windows再试。

3.2 ORA-28547连接失败

ORA-28547: connection to server failed, probable Oracle Net admin error这个错误我见过两种完全不同的场景。第一种是客户端应用连不上,典型原因是客户端sqlnet.ora里命名方式配置有问题,或者tnsnames.ora连接串写错;第二种是连接到外部过程(External Procedure)时出的,比如调用Java存储过程或某些驱动库。

如果服务端是19c、客户端还是11g的老OCI,很容易在连接阶段直接报ORA-28547。这种情况本质是客户端版本太老,与19c的Oracle Net协议不兼容。解决办法:升级客户端到至少12c以上,或者在服务器端sqlnet.ora里设置SQLNET.ALLOWED_LOGON_VERSION=8往后兼容,但要注意这个设置会降低认证安全性,属于临时方案。另外第三方工具连接不上时,先查驱动版本:DBeaver需要手动配置ojdbc8下载,Navicat则要确认自带的instant client版本足够新。

3.3 DG主备切换遇到resolvable gap

DG切换是老生常谈,但每次都有朋友问“resolvable gap”到底要不要紧张。先说结论:只要备库上能查出v$archive_gap有记录,就说明主备之间确实存在日志缺口,此时不能直接做switchover,必须先补gap。

处理gap的常规动作:

-- 备库查看gap select * from v$archive_gap;

拿到缺失的日志序号后,办法有三种:

  • 如果gap很小,直接把主库对应归档日志文件拷贝到备库,用ALTER DATABASE REGISTER LOGFILE '/path/arch/xxx.dbf';注册;
  • 如果gap较多或者文件很大,用RMAN的RESTORE ARCHIVELOG从主库拉取缺失归档,比自己手动拷文件更省事;
  • 如果备库落后实在太多,重建备库反而是最稳的选择,别硬追。

补完gap后执行ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;恢复应用。特别提醒:一主两备的场景下,切换前必须分别检查两台备库的gap,光确认某一台没用。failover到备库后,原主库要重建为新备库,这个流程提前演练过才不会手忙脚乱。

3.4 进入ASM的常用方式

升级或巡检时经常需要查看ASM磁盘组状态,很多新人卡在中等半天不知道用什么命令。简单说两种最常用的:

# 用grid用户登录,进入SQL*Plus并获取ASM管理权限 sqlplus / as sysasm # 或者在系统层面直接操作ASM文件 asmcmd ls

注意ASM的ORACLE_HOME必须指向GI的安装目录,而不是数据库的ORACLE_HOME,否则会提示权限不足或者找不到asmcmd。asmcmd里常用du查看磁盘使用率,find定位文件,cp跨磁盘组拷贝文件,基本满足日常巡检。

3.5 12c删除不干净导致新环境异常

这个问题在Windows上最多见。旧版本Oracle卸载后,注册表、服务项、目录残留都会干扰新的19c安装。常见现象是安装到一半报驱动冲突,或者装完后监听、实例无法启动。

Linux下的清理可以直接手动删:/etc/oratab/etc/oraInst.loc/etc/oracle/opt/ORCLfmap,以及整个ORACLE_BASE目录。Windows下的清理重点是注册表HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE,还有残留的服务,可以打开管理员命令行执行sc delete OracleServiceXXX。这些都属于“一次删干净,省得后面折腾”的典型经验。

4. 升级后附带的高频SQL问题速查

4.1 dual表别拿来当业务表用

群里有人问过“dual表最多能存多大数据”,这个问题本身就反映了对dual表定位的误解。dual是Oracle系统内部提供的一个单行单列表,主要用来执行select sysdate from dual这类不依赖业务表的查询,它不是给你存数据的。虽然在一些特殊版本里你能对dual执行update,但没有意义还会引发奇奇怪怪的并发问题。业务数据老老实实建业务表,dual表的事知道它是“一行一列”就够了。

另外经常有人分不清trunc(sysdate)to_char(sysdate)的用法。trunc(sysdate)返回的是日期时间,trunc(sysdate)是当天零点,trunc(sysdate, 'MM')是月初,trunc(sysdate, 'YYYY')是年初;而to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss')返回的是字符串,两者用途完全不同,SQL里混用会导致隐式转换问题,影响索引利用。

4.2 case when、逗号拆分、插入不重复

case when是Oracle里最常用的条件表达式,新手最常见的坑是把NULL写进简单case里:

-- 正确写法:搜索case select case when score >= 90 then 'A' when score >= 60 then 'B' else 'C' end as grade from student;

注意不要写成case NULL when NULL ...,NULL判断在case里永远不成立,必须用is null或者搜索case写法。

按逗号拆分列为多行也是高频需求。下面这条SQL可以快速实现:

select trim(regexp_substr('a,b,c', '[^,]+', 1, level)) as item from dual connect by level <= length('a,b,c') - length(replace('a,b,c', ',', '')) + 1;

“插入数据重复则不插入”这个问题,从MySQL转过来的朋友尤其容易踩坑,Oracle没有MySQL的ON DUPLICATE KEYON CONFLICT语法,正确写法是MERGE

merge into target_table t using (select :id as id, :name as name from dual) s on (t.id = s.id) when not matched then insert (id, name) values (s.id, s.name);

并发插入场景下,光靠MERGE还不够,目标表上要有主键或唯一索引兜底,否则两个会话同时判断“不存在”还是会插重。

4.3 MySQL与Oracle的语法差异速查

从MySQL切到Oracle,或者说现在很多库要从MySQL迁移到OceanBase再反推,语法差异是绕不开的。我整理了一份高频差异表:

功能MySQLOracle
分页LIMIT 偏移, 行数ROWNUM 或 OFFSET ... FETCH NEXT
空字符串允许空字符串空字符串''等价于NULL
字符串拼接CONCAT(),也支持||建议用||,CONCAT只能接两个参数
自增列AUTO_INCREMENT12c开始IDENTITY列,或SEQUENCE配触发器
日期当前值NOW() / CURRENT_TIMESTAMPSYSDATE / CURRENT_TIMESTAMP
条件函数IF(cond, a, b)CASE WHEN 或 DECODE
布尔类型BOOLEAN / TINYINT(1)NUMBER(1)
禁用索引ALTER INDEX ... DISABLEALTER INDEX ... UNUSABLE,重建用REBUILD
事务提交默认自动提交(无显式事务时)显式COMMIT,未提交不生效
存储过程抛异常SIGNAL SQLSTATERAISE_APPLICATION_ERROR

这里尤其注意分页和空字符串两点。Oracle 19c其实已经支持OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY这种标准写法,比老式的ROWNUM三层嵌套清晰得多:

select * from employees order by employee_id offset 20 rows fetch next 10 rows only;

空字符串的问题则是隐蔽的大坑,往Oracle表里插入''和插入NULL效果一样,这会导致很多从MySQL迁过来的应用在非空判断上出现诡异乱象,迁库时一定要扫一遍代码里的空字符串逻辑。

5. 升级避坑经验总结

最后分享几条我这些年做Oracle升级攒下来的经验。

第一,preupgrade.jar的结果一定要逐条读。这个工具判定的每一项风险都是前人踩坑换来的,比如它会提示某些表空间空间不足、某些待升级组件有已知bug、某些初始化参数需要调整。无视这些警告强行升级,后续大概率在某个凌晨两点出事。

第二,升级前准备回滚方案比准备升级方案更重要。RMAN备份是底线,如果库开启了闪回,建议升级前执行一次create restore point before_upgrade guarantee flashback database;,这样升级失败可以快速闪回到升级前状态,比从头恢复备份快太多。注意这个restore point是guarantee类型,会占闪回区空间,确认成功后记得删掉。

第三,不要一发布新RU就冲上去。补丁版本建议选择发布三个月以上的RU,让足够多的环境帮我们“试毒”。热词里出现的19.25.0.0.241015虽然新,但它是2024年10月发布,如今已经跑过相当长时间,属于可以放心用的成熟补丁。如果是刚发布两周的RU,除非有明确bug修复需求,否则别急着在生产上打。

第四,从11g升到19c后,应用侧回归测试的优先级要拉到最高。11g到19c跨越的版本多,隐含的认证变化、SQL执行计划变化、数据库优化器行为变化,都会让原本跑得好好的SQL突然变成慢SQL。升级完成后建议让性能测试环境先跑一轮压测,重点看TOP SQL的前后差异,再决定是否切生产。

第五,顺手把非CDB转成CDB/PDB。19c以后非CDB已经走在下坡路上,与其几次升级都重复改造,不如这次直接一步到位。最简单的方式是通过DBUA在升级时选择转换,或者用DBMS_PDB.DESCRIBE配合插拔PDB的方式完成改造。这条路径确实多花几个小时,但能帮你给未来几年的运维省下大把主动权。

我在实际项目里还有一个固定习惯:每做完一次升级,就把环境检查、升级命令、问题记录整理成一页纸checklist存下来,下次再升级直接照着走,不临时翻文档。尤其这次用catctl并行升级的日志,我会完整保留到归档目录,后面如果出问题排查,这些日志比什么工具都管用。19c升级的难度不在命令本身,而在于细节和顺序,把这篇文章里提到的坑提前排掉,你也能把升级窗口控制得稳稳当当。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询