☰
Oracle 11.2.0.3 PSU补丁:OPatch预检、apply与回滚
2026/11/4 12:28:27 网站建设 项目流程

简介:面向使用 Oracle Database 11g 11.2.0.3 的 Linux x86-64 环境数据库管理员与运维人员,这份补丁包对应编号 20760997 的数据库补丁集更新,即 11.2.0.3.15,并包含 2015 年 7 月 CPU 安全更新。其用途是集中修复已知安全漏洞、性能与稳定性问题,适合在测试验证后用于生产库的定期补丁维护、升级前评估及故障排查参考。压缩包约 100.73MB,上游暂未提供文件总数与类型明细,从描述可知内含补丁搜索配置或说明及补丁主体,便于核对适用性、依赖关系与安装前检查。已有 293 人学习或下载,说明该版本补丁在存量 Oracle 环境维护中仍有参考价值。读者可据此了解该补丁集的定位、涵盖的 CPU 内容及上线前需关注的前提条件,为补丁评估、备份规划与变更实施提供依据。

1. 一个 zip 摆在你面前:p20760997 到底该不该往生产库上打

凌晨收到一份p20760997_112030_Linux-x86-64.zip,多数人的第一反应是先解开看看里面有什么。文件名本身已经把大半信息交代清楚了:p20760997是补丁编号,112030指向 11.2.0.3 这条版本基线,Linux-x86-64说明它只认 64 位 Linux 上的 Oracle Home。这是一份典型的 oracle patch 集合包,属于 PSU/CPU 那一类累积补丁,打完以后数据库二进制和一份数据字典变更要一起生效,缺一半都不算打完。

它适合谁:还在跑 11.2.0.3、短期不做大版本升级、但要满足安全合规和已知 bug 修复的存量库。它有明确边界:跨版本基线打不进去,缺 OPatch 依赖也打不进去,没有回滚预案更不该在主库上动。接下来按我实际执行的顺序,把前置体检、冲突预检、apply 到字典更新、避坑和回滚一条条拆开。

2. 动手前先体检:OPatch 版本、Inventory 与磁盘空间的三道关

补丁出问题的现场,八成不是 apply 那一步出的,而是前面没体检。我见过最贵的一次停机,是 OPatch 版本差一个小版本号,apply 跑了二十分钟才报错退出,此时监听已停、应用已挂,只能尴尬地重新拉起实例再约窗口。所以这一章的三件事按顺序做完,才谈得上解压。

2.1 从文件名拆出补丁身份与版本边界

p20760997这个编号有两个用处:一是opatch apply时补丁目录名通常就是它,二是将来回滚时-id参数直接用它。112030对应 11.2.0.3,意味这份补丁只能落在 11.2.0.3 的 Oracle Home 上,硬往 11.2.0.4 或 12c 的 Home 上打,会在 prereq 阶段就被拒绝,报类似版本不匹配的错误。最后的平台标识决定它只适用于 Linux x86-64,混进 AIX 或 32 位环境连解压出来的二进制都用不了。

文件名片段含义落地时影响什么
p20760997补丁编号apply 的目录名、rollback 的-id
112030版本基线 11.2.0.3只能用于同版本 Oracle Home
Linux-x86-64操作系统与位数与uname -m、二进制位数必须一致
.zip压缩包解压后还需进一级才是补丁根目录

先用三条命令把环境钉死,别凭记忆。尤其是同时维护多套 Home 的机器,$ORACLE_HOME在哪个 shell 里指错了,后面全是白工。

# 确认当前 shell 指向的 Oracle Home 与版本基线 echo $ORACLE_HOME $ORACLE_HOME/OPatch/opatch lsinventory -detail | head -20 # 确认二进制位数与平台,排除 32 位 Home 混入的可能 file $ORACLE_HOME/bin/oracle uname -m

opatch lsinventory -detail的输出头部会列出 Oracle Home 路径、版本号、以及已安装的组件和补丁,这是后续对账的基线,建议直接重定向存一份。file那条是为了确认二进制是 ELF 64-bit,遇到从别的机器整目录拷贝过来的 Home,这一步能提前暴露问题。uname -m输出x86_64才和文件名里的平台标识对得上。

2.2 OPatch 版本不够,后面每一步都是白做

OPatch 是独立于数据库发布的东西,11.2.0.3 自带的版本往往比当年新补丁要求的低。补丁包里的 README 会写明最低 OPatch 版本要求,常见写法是要求在 11.2.0.3 系列下的某个小版本之上。判断方法很直接:

# 查看当前 OPatch 版本,四段式版本号 $ORACLE_HOME/OPatch/opatch version # 顺便看已装补丁列表,作为补丁前快照 $ORACLE_HOME/OPatch/opatch lspatches

opatch version输出形如OPatch Version: 11.2.0.3.x,拿这个 x 和 README 里的下限比。低于要求时,处理办法是把新版 OPatch 解压出来替换,但顺序不能反:先mv $ORACLE_HOME/OPatch $ORACLE_HOME/OPatch.bak,再把新包解压到$ORACLE_HOME下生成新的OPatch目录,最后chown -R回 oracle 属主。直接覆盖解压会留下新旧混杂的 jar 包,运行时报的错会非常难查,这是血泪经验。

提示:替换 OPatch 前务必确认没有其他会话正在使用它,把$ORACLE_HOME/OPatch改名这种操作在有人跑lsinventory的时候做,会让对方的命令直接失败。

还有一个容易被忽略的点:OPatch 的 inventory 指针。标准安装下它自己知道去哪找中央 inventory,如果这套环境是从别处迁移或克隆来的,可能需要-invPtrLoc指定oraInst.loc。判断方法是随便跑一条opatch lsinventory,能正常列出内容说明指针没问题,报 inventory 相关错误就先解决这个,别急着往下走。

2.3 Inventory、权限与空间:三个最容易漏掉的前置项

空间要算两笔账。第一笔是补丁解压目录,zip 解开后通常比压缩包大几倍,放/u01/patch这类数据盘上更稳妥,别放/tmp。第二笔是 Oracle Home 自身,OPatch 在 apply 时会把被替换的文件备份进$ORACLE_HOME/.patch_storage,这个目录的体积和补丁规模正相关,Home 所在文件系统至少要留出补丁体积两三倍的余量。

# 看 Home 与临时目录剩余空间,别只看百分比,看剩余 GB df -h $ORACLE_HOME /tmp # 看解压后的补丁体积,据此估算 .patch_storage 的增量 du -sh /u01/patch/20760997 # 确认 Home 属主与关键目录权限 ls -ld $ORACLE_HOME $ORACLE_HOME/OPatch

df -h的 Avail 列才是有意义的数字,有些环境/tmp挂在很小的根分区上,解压时直接撑爆,报错信息还很隐晦。du -sh拿到补丁体积后,按两到三倍预留 Home 空间是比较稳的做法。权限这一项检查的是 Oracle Home 必须归数据库软件属主所有,用 root 打补丁会留下 root 属主的文件,之后数据库启动或下次打补丁时读取失败,属于典型后患。

最后确认实例状态和应用侧窗口。补丁要在数据库关闭状态下 apply 二进制部分,停机窗口得提前跟业务对齐;同时把ps -ef | grep ora_的输出存一份,哪些后台进程在跑、哪些节点在活动,出问题时这是对照表。

3. 解压与冲突预检:把问题挡在 opatch apply 之前

解压这一步看起来最没技术含量,实际上补丁目录层级搞错是新手最高频的翻车点。zip 里往往还套一层同名目录,直接在外层跑 apply,会得到一句「不是有效的补丁位置」之类的提示,很多人在这里反复怀疑补丁包损坏,其实只是没进对目录。预检做扎实,apply 阶段基本就是等进度条。

3.1 解压路径、README 与补丁自带的 prereq 说明

# 单独建目录,避免和旧补丁混在一起 mkdir -p /u01/patch/20760997 cd /u01/patch/20760997 unzip /u01/soft/p20760997_112030_Linux-x86-64.zip # 看解压结果,确认层级 ls -l # 进入真正的补丁根目录,标志是能看到 etc/config 和 README cd 20760997 ls -l etc/config README*

unzip之后用ls确认层级,补丁根目录的判定标准是里面有etc/config目录和README.html或README.txt。这个判定标准记住,比记目录名靠谱。后面的-ph参数都指这个目录。

README 不要跳。至少精读四段:前置条件(Prerequisites)、OPatch 最低版本、安装步骤(Installation Steps)、以及 Known Issues。前置条件段会写明需要哪些组件、是否可以和其他补丁共存;安装步骤段会明确列出opatch apply之后要执行哪几个 SQL 脚本,这个脚本名在不同补丁上会变,凭经验猜是危险动作。Known Issues 里常有「本补丁与某 OJVM 补丁冲突」这类说明,提前看到能省一次窗口。

3.2 冲突检测与子系统补丁的先后关系

apply 之前至少跑三类 prereq 检查,它们都是只读操作,失败不留下任何痕迹。

cd /u01/patch/20760997/20760997 # 一:与当前 Oracle Home 已装补丁的冲突检测 $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./ # 二:系统空间是否满足补丁要求 $ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph ./ # 三:是否有活动进程占用将被替换的文件 $ORACLE_HOME/OPatch/opatch prereq CheckActiveFilesAndExecutables -ph ./

三条命令的-ph都指向补丁根目录(那个含etc/config的目录),写./是因为已经 cd 进去了。第一条输出结果是 Passed 或 Failed,Failed 时会列出冲突的补丁号;第二条校验 Home 和临时空间;第三条最容易失败,因为它会检查 Oracle 二进制是否正被进程占用,监听没停、实例没关都会在这里现形。

如果同一个维护窗口还要上 OJVM 那一类子系统补丁,顺序和参数不能凭感觉排。一般原则是先 RDBMS 后 OJVM,两边的字典变更脚本要分别执行,且 OJVM 的脚本参数和 RDBMS 不同。具体到某个补丁号,README 的冲突说明和上面第一条命令的输出是唯一依据,冲突报告里写着需要先回滚哪个补丁就先回滚,不要试图用-force绕过。

注意:-force这类参数在测试环境用来省事可以理解,生产环境用它等于把冲突检测白做了,后续出问题连复现路径都没有。

3.3 单机、RAC 与 Data Guard 的操作顺序差异

单机最简单:停监听、停实例、apply、起实例、跑字典脚本。RAC 要按节点滚动,顺序是先停一个节点的实例(srvctl stop instance -d <db> -i <inst>),在该节点执行opatch apply -local,起实例,再处理下一个节点。-local的作用是只更新本节点的 Home 和 inventory,不加这个参数在共享 Home 的 RAC 里会引发混乱。所有节点的二进制都打完之后,字典更新脚本只需要在其中一个节点执行一次,重复执行会报对象已存在的错误。

Data Guard 的顺序要更谨慎。常规做法是先在备库的 Oracle Home 上 apply 二进制(备库处于 mount 或关闭状态),再在主库 apply。关键在于:主备两边二进制版本不一致的这段时间里,备库无法应用主库的新归档,日志传输会堆积。所以主备的补丁要安排在同一个短窗口内完成,别今天打备库、下周打主库,中间靠归档堆积撑着的风险不小。

# RAC 单节点滚动示意:停实例 -> 本节点 apply -> 起实例 srvctl stop instance -d prodcdb -i prodcdb1 cd /u01/patch/20760997/20760997 $ORACLE_HOME/OPatch/opatch apply -local srvctl start instance -d prodcdb -i prodcdb1

停实例用srvctl而不是shutdown immediate,是为了让集群层面知道这个节点是计划内下线,不会触发实例拉起。-local只影响本节点,其余节点的 Home 状态不变,这也是滚动打补丁能成立的前提。做完一个节点,用下一节的验证命令确认没问题再动第二个,别一口气全停。

4. 正式 apply:从 opatch apply 到 catbundle.sql 的完整链路

真正执行的部分反而最短,前面铺垫到位了,这里就是三条命令加一次验证。但链路是完整的:二进制替换、inventory 登记、数据字典更新、无效对象重编译,四步缺一个都算没打完。很多「补丁打完了但系统行为没变」的现场,缺的就是字典那一步。

4.1 停库、冷备份与 opatch apply 实操

# 1. 关库并停监听,顺序别反 sqlplus -s / as sysdba <<'EOF' shutdown immediate exit EOF lsnrctl stop # 2. 记录补丁前状态,作为回滚与对账基线 $ORACLE_HOME/OPatch/opatch lspatches > /u01/backup/lspatches_before.txt $ORACLE_HOME/OPatch/opatch lsinventory -detail > /u01/backup/lsinv_before.txt # 3. 执行 apply,保留完整输出 cd /u01/patch/20760997/20760997 $ORACLE_HOME/OPatch/opatch apply 2>&1 | tee /u01/backup/apply_20760997.log

先关库再停监听,是因为反过来的话,本地连接可能还挂在监听上,CheckActiveFilesAndExecutables会拦下来。两条快照命令把补丁前的 inventory 和补丁列表存下来,将来要确认「到底装了哪些」时,这份文件比回忆可靠。

opatch apply的输出里要盯三处:补丁是否被判定为可应用(Checking if patch can be applied/reverted successfully)、有没有 warning、以及最后的OPatch succeeded。中途出现OPatch failed with error code就停下来看日志,别急着重跑,重跑前先确认.patch_storage里的残留状态。日志文件在$ORACLE_HOME/cfgtoollogs/opatch/下也有一份,tee出来的是额外保险。

有存储快照的环境,在 apply 之前给 Oracle Home 所在卷打一个快照,回滚时能整体回到打补丁之前的状态,比逐条 rollback 干净。没有快照的话,至少要保证.patch_storage不被清理,它是opatch rollback唯一依赖的东西。

4.2 数据字典更新:catbundle.sql、utlrp 与查询改写的影响

二进制替换完成后,启动数据库到 open 状态,然后按 README 指定的脚本执行字典更新。11.2.0.3 上这一步通常是catbundle.sql带上 psu 参数:

cd $ORACLE_HOME/rdbms/admin sqlplus -s / as sysdba <<'EOF' @catbundle.sql psu apply @utlrp.sql exit EOF

catbundle.sql带psu apply参数会更新registry$history、registry$sqlpatch等基表,并按补丁内容重编译部分组件;第二条utlrp.sql把更新过程中变成 INVALID 的对象重新编译回来。执行时数据库必须是 open 状态,只到 mount 会报错。RAC 里这个脚本只在最后一个节点跑一次,每个节点都跑会撞上对象已存在的报错。

脚本执行会在$ORACLE_HOME/rdbms/admin下留下catbundle_psu_开头的日志,出问题时先看这个日志里的 ORA- 错误。跑完utlrp.sql再看一次无效对象数量,正常情况下应该回到个位数或零,如果仍有大量 INVALID,通常是某个组件版本不一致,需要单独跑该组件的重编译脚本。

关于执行计划的变化,有个容易被忽略的点:PSU 本身不改optimizer_features_enable,但个别 bug 修复会改变优化器的选择。打完当天重点盯 top SQL 的执行计划有没有翻转,尤其是那种本来就贴着性能红线跑的语句。这不是补丁的锅,但补丁可能是压垮它的最后一根稻草。

4.3 打完之后怎么验证:三条命令对账

# 1. 补丁列表里应当能看到 20760997 $ORACLE_HOME/OPatch/opatch lspatches # 2. inventory 详情里确认补丁落在正确的 Home 上 $ORACLE_HOME/OPatch/opatch lsinventory -detail | grep -i 20760997
-- 3. 数据字典侧对账:历史记录里应有一条对应本补丁的 PSU 记录 select action_time, action, namespace, version, id, comments from registry$history order by action_time desc fetch first 10 rows only; -- 组件版本与状态 select comp_name, version, status from dba_registry order by comp_name; -- 无效对象数量,正常应接近零 select count(*) from dba_objects where status = 'INVALID';

对比这三处结果:lspatches有补丁号说明二进制和 inventory 到位,registry$history有记录说明字典脚本跑过,dba_registry里各组件状态为 VALID、无效对象接近零说明编译收尾完成。三处任何一处对不上,都属于「打了一半」,比如lspatches有而registry$history没有,就是忘了跑catbundle.sql,这种半成品状态在下一次打补丁时会以冲突的形式暴露出来。

fetch first 10 rows only是 11.2 之后的写法,如果环境版本更早可以换成rownum <= 10。这三条查询建议和 apply 前的快照放在同一个目录,形成一份完整的补丁档案。

5. 避坑与排查:五条现场记录

5.1 五类高频故障的现象、原因与处理

第一条:apply 刚启动就报 OPatch 版本过低。现象是命令执行十几秒后退出,提示当前 OPatch 版本不满足补丁要求。原因是 11.2.0.3 自带的 OPatch 比补丁发布时要求的旧。解决方式是先备份再替换 OPatch 目录,替换后重新跑一次opatch version确认,再执行 prereq 检查。别在原目录上覆盖解压,新旧 jar 混在一起后续报错更离谱。

第二条:提示不是有效的补丁位置。现象是opatch apply或 prereq 检查报找不到补丁元数据。原因几乎都是目录层级不对,在外层解压目录下执行,或者-ph指到了父目录。解决办法是cd进含etc/config的那一级再执行,或者把-ph明确指过去。这个错误和补丁包损坏的提示很像,先查层级再怀疑包。

第三条:apply 到一半因空间不足退出。现象是进度到某个百分比后报写入失败。原因是 Oracle Home 所在文件系统剩余空间不够.patch_storage的增量。解决是先清理无关的旧日志和旧补丁目录,确认df -h的 Avail 足够后再重新 apply。如果已经写了一半,用opatch lsinventory确认 inventory 没有被写脏,必要时按 README 的回滚步骤先清理。

第四条:CheckActiveFilesAndExecutables 失败。现象是冲突预检直接 Failed,列出被占用的二进制文件。原因是有后台进程还在跑,常见的是监听没停干净、或者别的实例还指向同一个 Home。解决是用ps -ef | grep $ORACLE_HOME找出占用进程,逐个停掉,确认fuser不再报占用后重跑预检。

第五条:二进制打了但行为没变。现象是lspatches能看到补丁号,但应用侧反馈某个问题依旧存在,或者registry$history里查不到对应记录。原因是跳过或漏跑了catbundle.sql。解决是补跑字典脚本并执行utlrp.sql,然后再对账一次。跳过字典更新的补丁在系统里就是个定时炸弹,下次打补丁时会以「补丁已安装」被拒绝,处理起来更麻烦。

注意:以上五条的共性都是「早发现早处理」。prereq 检查全部通过再 apply,能挡掉其中至少三条。

5.2 回滚预案:opatch rollback 能救什么、救不了什么

回滚二进制本身很简单:

# 关闭数据库后执行,-id 用补丁编号 sqlplus -s / as sysdba <<'EOF' shutdown immediate exit EOF $ORACLE_HOME/OPatch/opatch rollback -id 20760997

-id就是文件名里那串编号。执行前必须确认.patch_storage下对应补丁的备份目录还在,有些环境打完补丁顺手清理磁盘把.patch_storage删了,等到要回滚才发现没退路,这是最没必要的后悔药。

回滚能救的:Oracle Home 里的二进制、inventory 里的补丁记录。回滚救不了的:catbundle.sql已经改过的数据字典内容、被重编译过的对象、以及应用侧因为计划变化已经产生的影响。也就是说,二进制回滚完,数据库层面仍然是「打过补丁」的字典状态,两者不一致可能引发更奇怪的问题。

所以回滚预案的真正核心不是那条命令,而是打补丁之前的整体备份:Oracle Home 的存储快照、数据库的全备或数据字典相关表空间备份、以及一份lsinventory快照。有这三样,回滚才是一次可执行的操作;只有opatch rollback,那叫拆东墙补西墙。

6. 进阶:把一次打补丁固化成可复用的检查脚本

单次打补丁靠细心,长期维护靠脚本。下面这个是补丁前后各跑一次的快照脚本,输出落到带时间戳的目录里,用来做前后对账。

#!/bin/bash # patch_snapshot.sh —— 补丁前后各执行一次,用于状态对账 set -e TS=$(date +%Y%m%d_%H%M) OUT=/u01/backup/snapshot_${TS} mkdir -p "$OUT" # 1. 二进制与补丁列表 $ORACLE_HOME/OPatch/opatch version > "$OUT/opatch_version.txt" 2>&1 $ORACLE_HOME/OPatch/opatch lspatches > "$OUT/lspatches.txt" 2>&1 $ORACLE_HOME/OPatch/opatch lsinventory -detail > "$OUT/lsinventory.txt" 2>&1 # 2. 收一份关键文件的校验值,回滚后可比对 md5sum $ORACLE_HOME/bin/oracle > "$OUT/oracle_bin.md5" 2>&1 # 3. 字典侧状态,库需处于 open sqlplus -s / as sysdba <<EOF > "$OUT/db_state.txt" 2>&1 set pages 200 lines 200 select comp_name, version, status from dba_registry order by comp_name; select count(*) invalid_objects from dba_objects where status='INVALID'; select action_time, namespace, version, id from registry$history order by action_time desc; EOF echo "snapshot written to $OUT"

脚本里set -e保证任何一条命令失败就停,避免在环境不对的情况下产出一份看似完整的假快照。前三行把 OPatch 层面的状态全部落盘,md5sum那行是给二进制做指纹,回滚后重新算一次就能确认是否真的回到了原状。最后的 SQL 段把组件状态、无效对象数、字典历史一起导出,set pages和lines是为了让输出不被截断,否则长字段会被折行,比对时看着难受。

实际使用时,我会在打补丁前跑一次,打完catbundle.sql和utlrp.sql之后再跑一次,然后diff两个目录里的同名文件。历次经验里,lspatches.txt的差异应当只有新增的那一行,db_state.txt里无效对象数应当回落到基线附近,registry$history应当多出本次的记录。如果某个文件的差异超出了预期范围,比如组件版本莫名其妙变了、或者无效对象数量反而上升,那说明这一步有问题,宁可停下来查也不要继续往下走。

另一个值得固化的习惯是把补丁包和解压目录按补丁号归档,README 和 apply 日志放在一起,同时在归档目录里留一份「本次执行了哪些 SQL 脚本」的说明。半年后再回头查某个库的状态,这些文件比重读一遍文档管用得多。

从那以后我每次动手前,都会强制把这个快照脚本跑一遍,前后两份 diff 摆在面前才敢说这次补丁是真的打完了。希望帮到你。

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

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

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

立即咨询