
我先把这个项目的背景说清楚。我们刚给一套跑了八年的Oracle 11.2.0.4库完成了19c升级用的是目前较新的19.25.0.0.241015版本。整个升级过程下来最大的感受是升级本身没那么吓人真正让人反复折腾的是升级路径怎么规划、补丁版本怎么确认以及升级之后冒出来的监听起不来、ORA-28547、DG主备出现gap这类问题。这篇我不讲基础安装教程只讲从老版本升到19c怎么落地以及我这些年在群里、论坛里收到频率最高的几个顶级QA希望能帮你绕开几个大坑。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再升19c11.2.0.3否需要先补丁到11.2.0.411.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代表数据库主版本也就是19c25代表这是第25个季度的Release UpdateRU补丁版本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.sql和preupgrade_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_SIDORCL 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.sqlutlrp.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. 升级后高频QA监听、连接、DG这些坑3.1 监听服务无法启动这大概是19c升级后出现频率最高的故障。典型症状是lsnrctl start启动后立刻退出报错常见于TNS-12541、TNS-01189或者干脆日志里什么都没写。排查顺序我建议固定下来看$ORACLE_HOME/network/admin/listener.ora重点检查括号是否匹配、HOST那一项是否用了无法解析的主机名在服务器上先ping 主机名再cat /etc/hosts确认解析没问题检查1521端口是否被别的进程占用netstat -an | grep 1521在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_VERSION8往后兼容但要注意这个设置会降低认证安全性属于临时方案。另外第三方工具连接不上时先查驱动版本DBeaver需要手动配置ojdbc8下载Navicat则要确认自带的instant client版本足够新。3.3 DG主备切换遇到resolvable gapDG切换是老生常谈但每次都有朋友问“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 KEY或ON CONFLICT语法正确写法是MERGEmerge 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升级的难度不在命令本身而在于细节和顺序把这篇文章里提到的坑提前排掉你也能把升级窗口控制得稳稳当当。