☰
Oracle 11.2.0.4 PSU 220118 升级与回退实战
2026/10/27 7:58:27 网站建设 项目流程

简介:这份资源是面向 Oracle DBA 与数据库运维人员的 11.2.0.4 版本补丁集,对应 2022 年 1 月发布的 PSU(Patch Set Update),补丁号 33477185,适用于 Linux x86-64 平台的 64 位系统。其用途是修复已知安全漏洞,并累积性能优化与稳定性改进,适合需要为存量 11.2.0.4 数据库做季度补丁升级、补齐安全基线的运维场景,也便于研究 Oracle 补丁机制的技术人员参考。压缩包共 3658 个文件、约 436.52MB,以 .so 动态库与 .o 目标文件为主,配套 182 个 xml 元数据、142 个 sql 脚本及 plb、class、jar、mk 等组件;PatchSearch.xml 给出补丁适用条件与安装说明,与补丁号同名的目录则是实际安装主体,结构完整、依赖齐全。目前已有 861 人学习下载。借助它可完成 OPatch 冲突检查、补丁应用与回退验证,并对照 sql 脚本确认数据字典更新是否生效。

1. 从一次“补丁打完了、实例没起来”说起:11.2.0.4.220118 这版 PSU 到底装了什么

oracle 11.2.0.4 在很多老系统里还跑着,能补到的最新一档就是 11.2.0.4.220118(2022 年 1 月),Linux x86-64 平台上对应的补丁号是 p33477185。我见过最多的翻车不是补丁本身有毛病,而是机器上的 OPatch 还停在 11.2.0.3.12 这种老版本,opatch apply第一行就报 requires OPatch version,然后有人开始手工改 inventory、删 ORACLE_HOME 下的目录,最后连lsinventory都读不出来。这套环境的特点很明确:库不敢停、停机窗口两小时、没人敢背回退的锅,所以装之前必须把版本、备份、回退三件事依次落实。这篇记录写给还在维护 11g 的同行,照着走能一趟打完,出了问题也知道从哪一步往回退。

2. 版本对齐:把 11.2.0.4 和 OPatch 11.2.0.3.22 配成一对

补丁打不进去,九成原因在动手之前就注定了:OPatch 版本和补丁包不匹配、ORACLE_HOME 指错、inventory 权限不对。这三个问题在opatch apply阶段的报错都很含糊,看起来像补丁坏了,实际上是环境没对齐。所以第一步不是解压补丁,而是把家底摸清楚,把 OPatch 升到位,再让补丁包解压到一个干净的目录里。顺序错了,后面每一步都得返工。

2.1 三条命令摸清家底:库版本、OPatch 版本、inventory 位置

先用 oracle 用户登录,不要在 root 下做任何 OPatch 操作,11g 的 OPatch 是 Perl 脚本,用 root 跑出来的文件属主会变成 root,后面 datapatch 阶段会因为这个报权限错。环境变量只设当前会话,不要写进.bash_profile里长期留着一个可能过期的 ORACLE_HOME。

# 只在当前窗口生效,避免和其它版本的库串环境 export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 export ORACLE_SID=orcl export PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch:$PATH # 1) OPatch 版本,看是不是 11.2.0.3.22 及以上 $ORACLE_HOME/OPatch/opatch version # 2) 当前 ORACLE_HOME 里已经装过哪些补丁 $ORACLE_HOME/OPatch/opatch lsinventory -detail | head -40 # 3) inventory 指针,后面备份和排错都要用到这个路径 cat /etc/oraInst.loc

opatch version输出形如OPatch Version: 11.2.0.3.22,低于这个数就先别往下走。lsinventory -detail的输出里有三块要重点看:Oracle Home是不是你以为的那个路径、OPatch version、以及Patches installed in the Oracle Home下面已装的补丁列表。如果之前有人手工装过半截补丁,这里会留下痕迹,比如某个补丁只出现在lsinventory里但lspatches里没有对应记录,这种情况得先处理干净再打新补丁。/etc/oraInst.loc里的inventory_loc=指向中央 inventory,inst_group=通常是 oinstall,这两个值决定了 OPatch 有没有写权限,权限不对时报的是Inventory is corrupt这类吓人的话,实际只是组不对。

另外补一条 SQL 侧确认,v$version里的版本串只反映二进制版本,不反映 PSU 小版本,别拿它判断补丁打没打上:

select * from v$version; -- 11.2.0.4.0 这种四段式只能说明是 11.2.0.4 基线

2.2 OPatch 升级:p6880880 覆盖之前先改名

OPatch 升级包是 p6880880,11.2 平台对应p6880880_112000_Linux-x86-64.zip。常见做法是直接把压缩包解压到$ORACLE_HOME,压出来的 OPatch 目录会覆盖旧的;我不建议这么干,因为覆盖是逐文件写的,中途磁盘满或者中断,你会得到一个半新半旧的 OPatch,比不升还麻烦。我一般的做法是先整体改名留底,再解压。

cd /u01/soft # 先留一份旧的 OPatch,万一新版跑不起来还能切回去 mv $ORACLE_HOME/OPatch $ORACLE_HOME/OPatch.bak.$(date +%Y%m%d) # 解压到 ORACLE_HOME,压缩包内顶层目录就是 OPatch unzip -q p6880880_112000_Linux-x86-64.zip -d $ORACLE_HOME # 属主和权限必须修正,否则 datapatch 阶段写日志会失败 chown -R oracle:oinstall $ORACLE_HOME/OPatch chmod -R 775 $ORACLE_HOME/OPatch # 复查 $ORACLE_HOME/OPatch/opatch version

-d $ORACLE_HOME是关键参数,压缩包里只有一层OPatch/目录,解压到 ORACLE_HOME 才能落在正确位置。chown里的组要和/etc/oraInst.loc里的inst_group一致,不一致的时候lsinventory会报写不进去。改完名之后旧目录别急着删,打完补丁稳定运行一周再清理,这是成本最低的后悔药之一。

2.3 解压 p33477185:目录名就是补丁号,别解到 ORACLE_HOME 里

补丁包要解压到一个独立的 staging 目录,不要图省事解压到 ORACLE_HOME 下面。解压出来的目录名就是补丁号33477185,里面etc/config/inventory.xml记录了这个补丁会动哪些文件,README.html里有本次的前置条件和安装后动作,这两份东西在出问题的时候是唯一的判断依据。

mkdir -p /u01/soft/psu220118 cd /u01/soft/psu220118 unzip -q /u01/soft/p33477185_112040_Linux-x86-64.zip ls -l 33477185 # 至少把 Prerequisites 和 Post-install 两段过一遍 grep -i -A5 "prerequisite" 33477185/README.html | head -40

解压完先看README.html里的三件事:需要的 OPatch 最低版本、是否需要先装其它前置补丁、安装后是否需要跑额外脚本。11.2.0.4.220118 是累积型 PSU,正常情况下不需要先装旧 PSU,但如果 ORACLE_HOME 里装过某些一次性补丁(one-off),就可能撞车,这就是后面CheckConflictAgainstOHWithDetail要查的内容。

这一章最后给一张对照表,装机前逐项确认,任何一项打不上勾就别往下走:

检查项期望值查看方式
数据库基线版本11.2.0.4select * from v$version;
OPatch 版本11.2.0.3.22 及以上opatch version
补丁包平台Linux x86-64,p33477185解压后目录内etc/config
inventory 属组oinstall,且 oracle 用户可写cat /etc/oraInst.loc
/tmp剩余空间1GB 以上df -h /tmp
ORACLE_HOME 剩余空间3GB 以上df -h $ORACLE_HOME

3. 备份与回退:动手前先把备用回退方案备好

打补丁这个动作本身是覆盖 ORACLE_HOME 里的文件,不碰数据文件,理论上回退只要把 OPatch 装回旧版本就行。但现实里回退的难点从来不是opatch rollback这条命令,而是你回退的时候才发现没有可用的冷备、归档日志被清过、或者中央 inventory 已经被改乱。所以这一章的三件事是绑定在一起的:ORACLE_HOME 冷备、数据库可恢复备份、前置冲突检查,缺一样就别开窗口。

3.1 ORACLE_HOME 冷备:tar 要连 inventory 一起备

冷备的原则是“能原样还原回去”,所以备份范围要包括 ORACLE_HOME 和中央 inventory 两处。ORACLE_HOME 整个打包体积不小,常见的取舍是排除log、rdbms/log、admin这类纯日志目录,保留bin、lib、rdbms、OPatch、inventory、network、sqlplus。

mkdir -p /backup/psu220118 # ORACLE_HOME 冷备,排除日志类目录,压缩后一般几 GB tar -czpf /backup/psu220118/dbhome1_before_psu.tar.gz \ --exclude='log' \ --exclude='rdbms/log' \ --exclude='*.trc' \ -C /u01/app/oracle/product/11.2.0 dbhome_1 # 中央 inventory 一起备,回退时如果 inventory 损坏只能靠这个 cp -a /u01/app/oraInventory /backup/psu220118/oraInventory_bak ls -lh /backup/psu220118/

--exclude用相对路径匹配,写rdbms/log比写绝对路径更稳,因为tar记录的是-C之后的相对路径。cp -a保留权限和属主,inventory 里有ContentsXML和inventory.xml,属主错了opatch会直接拒绝工作。备份完一定要做一次tar -tzf抽查,确认关键文件在包里,别等到回退时才发现备份是空的。

3.2 数据库侧备份:归档日志在窗口期内一张都不能删

PSU 不打数据文件,所以 RMAN 全备从技术上讲不是必需的,但回退 ORACLE_HOME 之后重新拉起实例,可能需要重跑一次 catbundle 和 utlrp,这个过程里如果出现意外,有一份能开库的备份和完整的归档日志,才有资格谈恢复。11g 的老库如果归档空间紧张,常见做法是在打补丁前做一次全备并把归档清到备份里。

rman target / <<'EOF' backup as compressed backupset database plus archivelog delete input; delete noprompt obsolete; EOF

plus archivelog delete input的意思是备份完归档就删掉,这样后面的 redo 还能继续写。如果你不确定回退流程走得通,把归档留到补丁验证通过之后再清,别在窗口开始前就把退路删光。备份完成后用list backup summary看一眼,确认最近一次全备的时间戳是今天的。

3.3 冲突检查:两条 prereq 命令的结果怎么读

真正开窗口前,必须在补丁目录下跑两条前置检查。这两条命令不会改任何东西,但能提前把 90% 的失败挡在窗口之外。

cd /u01/soft/psu220118/33477185 export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 # 检查这个补丁和 ORACLE_HOME 里已装的补丁有没有文件冲突 $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./ # 检查补丁要覆盖的文件有没有被进程占用 $ORACLE_HOME/OPatch/opatch prereq CheckActiveFilesAndExecutables -ph ./

-ph ./指当前目录就是补丁目录,必须cd进去再执行,参数写成绝对路径有时会解析不到etc/config。第一条命令的通过标志是Prereq "CheckConflictAgainstOHWithDetail" passed.;如果报冲突,说明 ORACLE_HOME 里装过同名的 one-off 补丁,常见做法是先把那个 one-off 回退掉再打 PSU,而不是强行-force。第二条命令的通过标志是Prereq "CheckActiveFilesAndExecutables" passed.,它失败基本只有一种原因:库或监听还在跑,libclntsh.so被占用。这条失败别急着加参数跳过,老老实实停服务就行。

4. 正式打补丁:opatch apply 到 datapatch 的完整流程

前面三步是准备,这一步才是真正开始打补丁。整个流程按顺序是:停掉所有会占用 ORACLE_HOME 文件的进程 →opatch apply覆盖二进制 → 启动实例 →datapatch -verbose注册 SQL 层变更 → 编译失效对象 → 起监听和集群资源。中间任何一步失败都不要跳过去做下一步,尤其是opatch apply失败之后直接跑datapatch,会把 SQL 层和二进制层打成两个版本,后面的排查成本翻倍。

4.1 停服务:实例、监听,以及 RAC 下的先后顺序

单实例的停法很直接,先停监听再停实例,或者反过来都行,关键是最后要确认没有 oracle 用户的进程还挂在 ORACLE_HOME 上。

# 1) 停监听 lsnrctl stop # 2) 停实例 sqlplus -S / as sysdba <<'EOF' shutdown immediate; exit EOF # 3) 确认没有残留进程占用 ORACLE_HOME 下的文件 ps -ef | grep -E "ora_|tnslsnr" | grep -v grep

第三步不能省。shutdown immediate返回之后,后台进程有时候还要几秒才真正退出,pmmon、dbw 这类进程残留会让CheckActiveFilesAndExecutables继续报占用。等ps输出为空再执行 apply。

RAC 环境要额外注意顺序:先打 GI 侧的 PSU,再逐个节点打 DB 侧的 PSU,不能这个节点 DB 补丁还没打完就跑去打另一个节点。每个节点打完datapatch并验证通过之后,再切到下一个节点,这样出问题时至少还有一半节点能提供服务。集群资源用srvctl stop instance -d <库名> -i <实例名>停实例、srvctl stop listener停监听,停完用crsctl stat res -t确认目标节点上的资源都是 OFFLINE。

4.2 opatch apply:三个参数的实际取舍

apply 阶段我一般只用-local -silent两个参数,交互式提示在脚本化窗口里容易卡住,-silent让它一路跑完;-local表示只处理本地节点的 inventory,RAC 下必须加,不加会尝试去动远端节点。

cd /u01/soft/psu220118/33477185 export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 $ORACLE_HOME/OPatch/opatch apply -local -silent echo "exit code: $?"

正常结束会打印Patch 33477185 successfully applied.,并且末尾提示Log file location: /u01/app/oracle/product/11.2.0/dbhome_1/cfgtoollogs/opatch/...。这个日志路径记下来,失败的时候所有细节都在里面,比终端输出全得多。

如果输出里出现OPatch found the word "warning" in the stderr messages,不要直接忽略。11g 打 PSU 时这条警告经常是因为make阶段某些可选组件没编译,也可能是某个.so复制失败,原则是进日志里 grep 一遍ERROR和Failed,确认没有实质性失败再继续。中途失败的场景,正确的动作是看日志定位、按需要opatch rollback -id 33477185回退,然后重来一次,不要在失败状态上二次apply。

4.3 datapatch:SQL 层注册这一步不能省

opatch apply只换了二进制,SQL 层的字典变更要靠datapatch跑catbundle.sql完成。11.2.0.4 开始 datapatch 已经是标准动作,不需要手工去跑catbundle.sql。执行前数据库必须处于 OPEN 状态,因为 datapatch 要读dba_registry判断当前 SQL 补丁状态。

export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 export ORACLE_SID=orcl export PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch:$PATH # 启动到 OPEN sqlplus -S / as sysdba <<'EOF' startup; exit EOF # 注册 SQL 补丁,-verbose 会打印每一步 cd $ORACLE_HOME/OPatch ./datapatch -verbose

-verbose会依次打印Patch 33477185的apply过程、每个组件的upgrade状态,最后一行是patch applied successfully。datapatch 是幂等的,中途断了可以直接重跑,它会从上次停下的地方继续。跑完用下面这条 SQL 核对,STATUS必须是SUCCESS:

select patch_id, action, status, action_time, description from dba_registry_sqlpatch order by action_time desc;

如果STATUS是FAILED,日志在$ORACLE_HOME/cfgtoollogs/sqlpatch/下面,按时间找到最新的一份,里面会明确写出是哪条 SQL 报了什么错。11g 上常见的失败是表空间不足或者SYSTEM表空间不能自动扩展,扩容之后重跑datapatch -verbose就能续上。

4.4 编译失效对象,按正确顺序把服务拉起来

datapatch 跑完会有一批对象失效,这是正常的,用utlrp.sql编译一遍,然后再起监听和集群资源,顺序不要颠倒,否则应用连上来的时候会遇到一堆 ORA-04063。

sqlplus -S / as sysdba <<'EOF' @?/rdbms/admin/utlrp.sql select count(*) from dba_objects where status = 'INVALID'; exit EOF lsnrctl start

@?里的问号是 SQL*Plus 的 ORACLE_HOME 缩写,等价于$ORACLE_HOME/rdbms/admin/utlrp.sql。编译完成后失效对象数量不一定是 0,11g 的某些系统对象(比如SYS下的一些视图)长期是 INVALID 状态,判断标准是“打完补丁后这个数字不比打补丁前明显变多”,所以打补丁前先跑一次同样的查询把基线记下来,这一条很多人会漏。

RAC 环境下最后一步是把资源拉起来,用srvctl start instance和srvctl start listener,然后用crsctl stat res -t确认所有资源回到 ONLINE。

5. 避坑与常见问题排查:五条实际踩过的错误

下面这五条是 11.2.0.4 打 PSU 时重复出现频率最高的,按“现象、原因、解决”三段记录,遇到的时候可以直接对号入座。

5.1 apply 一上来就报 requires OPatch version

现象:opatch apply第一行输出Patch 33477185 requires OPatch version 11.2.0.3.22.,然后直接退出,一个文件都没改。

原因:机器上的 OPatch 还是数据库安装介质里带的旧版本,11.2.0.4 早期装的库很多停留在 11.2.0.3.6 或 11.2.0.3.12,而 2020 年之后发布的 PSU 都要求 11.2.0.3.22 以上。

解决:按第 2.2 节替换 p6880880,改名留底、解压、chown到 oinstall 组,再opatch version复查。切记不要为了绕过这条检查去改补丁包里的inventory.xml或者加什么跳过参数,版本不够就是真的跑不动。

5.2 CheckActiveFilesAndExecutables 报 libclntsh.so 被占用

现象:前置检查或apply过程中报某个进程占用了$ORACLE_HOME/lib/libclntsh.so,提示先停掉相关进程。

原因:数据库实例或监听还在运行,也可能是之前停库没停干净,残留了ora_pmon、ora_dbw这类后台进程;还有一种容易被忽略的情况是同一台机器上另一个 ORACLE_HOME 的客户端进程加载了这个库。

解决:先lsnrctl stop,再shutdown immediate,然后用ps -ef | grep $ORACLE_HOME逐个确认。如果确实有第三方程序连着这个 ORACLE_HOME 的客户端库,只能等业务停掉之后再打,这种情况没有技术上的绕法。

5.3 datapatch 报数据库没打开

现象:datapatch -verbose直接输出数据库未打开的错误,一条 SQL 都没执行。

原因:实例还在 MOUNT 或者 NOMOUNT 状态;另一个高频原因是ORACLE_SID没设或设错,datapatch 连到了另一个实例上,那个实例恰好没开。

解决:先sqlplus / as sysdba执行startup到 OPEN,再用select instance_name, status from v$instance;确认连的是哪个库,然后带正确的ORACLE_SID重跑。多实例机器上这一步尤其要确认,我见过在测试机上跑到了另一个库的 datapatch,最后花了两小时复盘。

5.4 打完之后应用连不上,报 ORA-01034

现象:补丁打完、实例也起来了,本地sqlplus / as sysdba正常,应用端连过来报 ORA-01034 或 ORA-12514。

原因:监听没起,或者监听配置文件读的还是补丁前的状态;RAC 环境下是资源没拉起,VIP 和 SCAN 没跟上。

解决:lsnrctl status看监听是否在跑、注册了哪些服务,必要时lsnrctl reload重读listener.ora。RAC 用crsctl stat res -t看资源状态,逐个srvctl start拉起。这类问题在验证章节里再过一遍会更稳。

5.5 datapatch 中途失败,状态卡在 FAILED

现象:dba_registry_sqlpatch里最新一条记录STATUS是FAILED,重跑datapatch -verbose仍然失败。

原因:绝大多数是 SQL 执行条件不满足,比如SYSTEM表空间满、UNDO不够、或者某个组件状态异常导致catbundle里某一句报错。

解决:先去$ORACLE_HOME/cfgtoollogs/sqlpatch/找最新日志,定位到具体报错的那条 SQL,把前置条件修好(扩表空间、清回收站、修组件状态),再重跑datapatch -verbose。不要急着opatch rollback,SQL 层失败一般不需要回退二进制,回退反而会引入新的不确定性。

6. 验证跑通:五条命令确认这套补丁真的生效

打补丁最容易自欺欺人的地方是“终端打印了 successfully 就当成功了”。我判断一套 PSU 是否真正生效,固定跑下面这几条,全部对齐才算收工。

# 1) 二进制层:补丁在不在 ORACLE_HOME 里 $ORACLE_HOME/OPatch/opatch lsinventory | grep -i 33477185 # 2) 补丁清单,看唯一补丁标识和描述 $ORACLE_HOME/OPatch/opatch lspatches | head -5
-- 3) SQL 层注册状态,必须是 SUCCESS select patch_id, action, status, action_time from dba_registry_sqlpatch; -- 4) 组件状态,正常应该没有 INVALID 的组件 select comp_name, version, status from dba_registry where status <> 'VALID'; -- 5) 失效对象数量,和打补丁前的基线对比 select count(*) from dba_objects where status = 'INVALID';
检查项期望结果不对时先看哪里
lsinventory含 33477185出现补丁号$ORACLE_HOME/cfgtoollogs/opatch/最新日志
lspatches首行33477185 且描述对应 220118是否错打了别的补丁
dba_registry_sqlpatch最新记录 SUCCESS$ORACLE_HOME/cfgtoollogs/sqlpatch/
组件状态全部 VALID单个组件日志,必要时重跑该组件脚本
失效对象数不明显多于基线重跑utlrp.sql,看编译报错

还有一条实战技巧:验证不要只在本地sqlplus里跑,找一台应用侧的机器用业务账号连一次,确认监听注册、服务名解析、密码校验都正常,这比在数据库主机上跑一百遍 SQL 更能暴露问题。我在一次窗口里就是因为只验了本地,应用侧连过来才发现监听没 reload,白等了四十分钟。

从那以后我每次打完补丁,都强制把lspatches和dba_registry_sqlpatch两条输出原样存进变更记录,附上时间戳和执行的 ORACLE_SID,下次再出问题,翻记录比翻记忆靠谱得多。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询