简介:面向仍维护 Oracle Database 11.2.0.4 的 DBA 与运维人员,这份 2022 年 1 月发布的 PSU(补丁号 33477185)适用于 Linux x86-64,可用于该版本数据库的安全修复、已知问题规避与稳定性维护。压缩包共 3658 个文件、约 436.52MB,以 so 共享库、o 目标文件、xml 元数据、sql 脚本及 jar/class 等为主,较完整保留补丁介质结构与组件依赖。已有 861 人学习下载,说明其在旧版本数据库维护场景中仍有参考价值。需要核对补丁组成、确认平台适配、留存离线介质或研究 Oracle PSU 目录组织的读者,可从中获得完整的一手材料。其目录层级也便于结合版本环境梳理补丁内容。
1. 从 11.2.0.4.220118 说起:那个被误读成补丁号的日期
上周又被叫去处理一套跑了七八年的老库,扫描报告要求把 Oracle 的补丁级别拉到 2022 年 1 月。有人把文件名甩到群里:DB-PSU-11.2.0.4.220118 (Jan 2022)-p33477185Linux-x86-64,第一反应是“220118 这个补丁号好大”,其实那六位是 2022 年 01 月 18 日,真正的补丁号是后面的 33477185。这种误读很常见,更常见的是 zip 解压、opatch apply敲完就收工,结果lsinventory里明明有补丁,应用连上去还是老行为。11.2.0.4 在存量系统里太普遍,这版 PSU 只针对 Linux x86-64 的数据库 home,要动的东西不少:OPatch 版本、中央清单、catbundle、无效对象,漏一步就得回滚。下面按我实际打这套补丁的顺序来。
2. 动手前的环境摸底:DB home、OPatch 版本与补丁编号的对应关系
补丁打到一半发现平台不对,是最浪费时间的翻车方式。11.2.0.4 的存量环境通常不止一个 home:有跑数据库的,有只装了客户端的,还有早年留下的测试 home,/etc/oraInst.loc指着的中央清单里往往挂着三四个。第一步要确认你登录的这台机器、这个 shell、这个ORACLE_HOME,就是待会要停库的那个,别停在 A home 上,把补丁打到 B home 里。
2.1 三条命令确认面对的是同一套环境
先别急着unzip,用真正跑库的那个用户(一般是 oracle,不是 root)把身份和平台确认清楚。
# 确认当前用户、SID 和 HOME,三个都要对得上 whoami echo "ORACLE_HOME=$ORACLE_HOME" echo "ORACLE_SID=$ORACLE_SID" # 补丁包是 Linux x86-64 的,位数不对直接放弃 uname -m getconf LONG_BIT # home 自带 sqlplus 的版本,能跑说明环境变量没串 $ORACLE_HOME/bin/sqlplus -vuname -m返回x86_64、getconf LONG_BIT返回 64,这两个对不上就说明补丁包下错了。文件名里的Linux-x86-64不是装饰,32 位 home 上opatch会在 prereq 阶段直接拒绝,报的还是“not applicable”,容易误判成补丁本身有问题。
进库再看一眼真实版本和是否 RAC,因为后面停库、打补丁、跑 catbundle 的步骤完全不同:
set lines 200 pages 200 col banner format a70 select banner from v$version; -- 返回 TRUE 就是 RAC,需要走 srvctl 那套停库姿势 select value from v$option where parameter = 'Real Application Clusters';banner显示Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production就对了。11.2.0.4 的 banner 本身不带 PSU 号,别指望从这里看出补丁级别,补丁信息在registry$history和opatch lsinventory里。
2.2 OPatch 版本不够就先换 OPatch
这是最容易忽略的一步。OPatch 是独立于数据库二进制的工具,2022 年的 PSU 放在 2014 年的 OPatch 上跑,大概率会在 apply 阶段报“OPatch failed with error code 73”或者干脆认不出补丁格式。
# 先看现在是多少 $ORACLE_HOME/OPatch/opatch version # 顺手确认 opatch 真的属于这个 home ls -l $ORACLE_HOME/OPatch/opatch补丁包里带的 README 会写最低 OPatch 版本要求,11.2.0.4 分支近几年的 PSU 一般要求 11.2.0.3.28 以上。我一般不看当前版本是多少,直接按 README 换成它列出的那个版本,省得反复试。换 OPatch 的动作不复杂,但顺序不能反:先备份、再解压覆盖、最后改属主。
# 1) 备份老 OPatch,带日期,出问题能立刻退回去 mv $ORACLE_HOME/OPatch $ORACLE_HOME/OPatch.bak.$(date +%Y%m%d) # 2) 解压新 OPatch 到 ORACLE_HOME 根目录,压缩包内层就是 OPatch/ unzip -q /u01/soft/p6880880_112000_Linux-x86-64.zip -d $ORACLE_HOME # 3) 属主必须是 home 的属主,root 解压出来是 root:root,后面 apply 会写不进清单 chown -R oracle:oinstall $ORACLE_HOME/OPatch chmod -R 775 $ORACLE_HOME/OPatch # 4) 复核 $ORACLE_HOME/OPatch/opatch version-d $ORACLE_HOME这个参数要留意:如果写成-d $ORACLE_HOME/OPatch,解出来会变成OPatch/OPatch/opatch,命令照样能跑,但下次再换 OPatch 时你自己都分不清哪层是有效的。第 3 步的chown不是可选项,root 解压后不加属主,opatch apply会在写中央清单时失败,报的是“Unable to write to inventory”,看着像权限问题,其实是属主问题。
换完 OPatch 如果报“Unable to find a valid Java”,先看$ORACLE_HOME/jdk/jre/bin/java是否可执行;实在找不到,用-jre参数显式指过去。常见做法是优先用 home 自带的 jre,别去系统里另装 JDK,版本对不上反而更麻烦。
2.3 补丁解压到哪、归谁
补丁包不要解到$ORACLE_HOME下面,一是 home 分区本来就紧张,二是清理时容易顺手删错。我习惯单独建一个目录,按补丁号命名。
mkdir -p /u01/soft/psu/33477185 unzip -q /u01/soft/p33477185_112040_Linux-x86-64.zip -d /u01/soft/psu/33477185 # 注意 chown 的是解压出来的顶层,不是 zip 文件本身 chown -R oracle:oinstall /u01/soft/psu/33477185 # 看一眼目录结构,确认 etc 和 files 在同一层 ls /u01/soft/psu/33477185/33477185正常结构里应该有README.html、etc/、files/三样东西,etc下有patch_info.xml。opatch apply必须在同时包含etc和files的那一层执行,多进一层或退一层都会报“The patch location does not contain a valid patch”。
提示:补丁包和随包的 README 要一起拿。只有 zip 没有 README,OPatch 版本要求、回滚顺序、是否需要额外跑脚本这些信息就全凭猜,风险太高。
2.4 补丁冲突与空间预检
真正开始前有两项检查值得跑,都是opatch prereq的子命令,能提前把问题挡掉。
cd /u01/soft/psu/33477185/33477185 # 检查补丁与当前 home 已装补丁是否冲突 $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./ # 检查空间是否够用(老版本 OPatch 没有这个子命令,换成 2.2 的新 OPatch 就有) $ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -phBaseDir ./第一条返回Prereq "checkConflictAgainstOHWithDetail" passed才算干净。如果报冲突,通常是 home 上已经装了更高版本的同类 PSU,此时只能先回滚高版本再装这个,不能硬来。第二条是很多人会跳过的,opatch apply会把被替换的文件备份进$ORACLE_HOME/.patch_storage,占用的空间往往是补丁本体的好几倍,home 分区只剩两三个 G 的时候就别硬上。
顺手再看一眼临时目录。opatch会在TMPDIR下展开补丁,/tmp只有几个 G 的机器上中途报“No space left on device”很常见,提前指到数据盘上:
df -h $ORACLE_HOME /tmp # 临时目录挪到空间充足的分区,只对当前会话生效 export TMPDIR=/u01/tmp mkdir -p $TMPDIR环境摸底的结论记成一张表,后面出问题回头对一遍就知道哪一项变了:
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 数据库版本 | select banner from v$version; | 含 11.2.0.4.0,64bit |
| 平台位数 | uname -m/getconf LONG_BIT | x86_64 / 64 |
| OPatch 版本 | opatch version | 不低于 README 要求 |
| 补丁冲突 | opatch prereq CheckConflictAgainstOHWithDetail | passed |
| home 剩余空间 | df -h $ORACLE_HOME | 补丁体积的 3~5 倍以上 |
| 是否 RAC | select value from v$option ... | FALSE 走单实例流程 |
3. 停库与备份:动手前必须留的两个后悔药
补丁能回滚,数据回不来。这套流程里我坚持两个动作:打完补丁前必须有可用的数据库备份,ORACLE_HOME 至少留一份可对照的快照。前者保证数据安全,后者保证补丁打崩了能在半小时内退回原状,而不是重装 home。
3.1 停业务的顺序:dbconsole、监听、实例、集群
停的顺序错了会出现“库停了但文件还被占着”的情况,opatch apply替换rdbms/lib下的库文件时可能报“Text file busy”。
单实例的顺序是这样:先停 dbconsole(如果装了),再停 home 里的监听(如果是 GI 管理的监听则不用动,它不属于 DB home),最后停实例。
# 1) 停 dbconsole,没装会报命令不存在,忽略即可 $ORACLE_HOME/bin/emctl stop dbconsole # 2) 停 home 内的监听(GI 管理的监听不要停) $ORACLE_HOME/bin/lsnrctl stop # 3) 停实例,用 immediate,别用 abort $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'EOF' shutdown immediate exit EOFshutdown immediate之后要确认实例真的停了,ps -ef | grep pmon里应该看不到对应 SID 的进程。abort虽然快,但下次启动要走实例恢复,打完补丁第一次起库就多一段不可控的恢复时间,没必要。
RAC 环境用 srvctl 一次停整个库,别一个节点一个节点手动 shutdown:
# 停整库,-o immediate 传给每个实例 $GRID_HOME/bin/srvctl stop database -d orcl -o immediate $GRID_HOME/bin/srvctl status database -d orclstatus输出里所有实例都应该是Instance is not running。如果还有节点在跑,说明有实例卡住,先查alert日志再决定要不要强停,别在实例没停干净的时候打补丁。
3.2 RMAN 全备与 ORACLE_HOME 冷备
备份这件事没得商量。哪怕这套库昨天刚备过,打补丁前我还会再跑一次全备加归档,因为回滚补丁之后需要的是“补丁前那一刻”的数据状态。
rman target / <<'EOF' run { allocate channel c1 device type disk; allocate channel c2 device type disk; -- 压缩备份集,省空间也省传输时间 backup as compressed backupset database plus archivelog delete input; backup current controlfile; release channel c1; release channel c2; } EOF两个通道是按磁盘数量估的,单盘机器上写一个就行,多了反而互相抢 IO。plus archivelog delete input里的delete input要在确认备份成功后再用,如果归档目录还要留给别的流程,把这半句去掉。
ORACLE_HOME 的备份看时间窗口。home 通常十几到几十个 G,tar一遍半小时起步,比较快的做法是cp -a或者直接只备份关键目录:
# 完整冷备,耗时长但最稳,补丁打崩了直接解回来 tar -czpf /u01/backup/oracle_home_$(date +%Y%m%d).tar.gz \ -C /u01/app/oracle/product 11.2.0 # 只备关键部分:清单、网络配置、参数文件、可执行文件 cp -a $ORACLE_HOME/network/admin /u01/backup/network_admin_$(date +%Y%m%d) cp -a $ORACLE_HOME/dbs /u01/backup/dbs_$(date +%Y%m%d)tar一定要加-p保留权限,ORACLE_HOME 下不少文件带着 setuid 位,恢复时权限丢了会出现“实例起不来但日志没报错”的玄学现象。中央清单也别落下:
# 中央清单路径从 oraInst.loc 里读,别猜 cat /etc/oraInst.loc cp -a /u01/app/oraInventory /u01/backup/oraInventory_$(date +%Y%m%d)3.3 记录基线:lsinventory 和 registry 快照
打完补丁之后要证明“确实生效了”,就必须有打之前的对照。这两份快照每份都只花几十秒,但排障时能省几个小时。
# 完整清单,包含已装补丁列表 $ORACLE_HOME/OPatch/opatch lsinventory -detail \ > /u01/backup/lsinventory_before_$(date +%Y%m%d).txt-- 补丁注册历史,打完之后和这份对比就知道有没有新记录 spool /u01/backup/registry_before.txt set lines 200 pages 200 col comments format a40 select to_char(action_time,'YYYY-MM-DD HH24:MI') t, action, namespace, version, id, bundle_series, comments from registry$history order by action_time; -- 无效对象数量,打完补丁后要和这个数量对齐 select count(*) invalid_objects from dba_objects where status = 'INVALID'; -- 各组件版本状态 select comp_id, version, status from dba_registry; spool offregistry$history是 11.2.0.4 里判断补丁是否真正入库的核心视图,dba_registry_history是它的视图封装,查哪个都行。invalid_objects的基线尤其重要,老库里本来就有一堆失效对象,不记基线的话,打完补丁看到几百个 INVALID 根本分不清是新产生的还是历史遗留。
4. opatch apply 实操:从解压目录到 Success 提示
前面三板斧做完了,真正替换二进制文件只有几分钟。这几分钟里 opatch 会问两三个交互问题,答错一个就白等。
4.1 单实例 apply:一行命令背后的四个确认
cd /u01/soft/psu/33477185/33477185 # 必须在这一层执行,etc 和 files 的父目录 $ORACLE_HOME/OPatch/opatch apply回车之后会有几处需要确认,逐条说。第一处是“Do you want to proceed? [y|n]”,输 y。第二处如果中央清单路径不对,会提示输入 inventory 位置,这时候用-invPtrLoc /etc/oraInst.loc重新执行更稳妥:
# 清单位置异常时显式指定,避免 opatch 去读默认路径下的旧清单 $ORACLE_HOME/OPatch/opatch apply -invPtrLoc /etc/oraInst.loc第三处是询问是否已经有实例在运行,这一步如实回答。如果前面没停干净,这里回答“有”,opatch 会直接终止,不会硬改文件,这是它比手工替换可靠的地方。第四处是最后的 apply 确认,回车后开始复制文件。
整个过程的输出建议全程留档,别只盯着最后那行:
$ORACLE_HOME/OPatch/opatch apply -invPtrLoc /etc/oraInst.loc 2>&1 \ | tee /u01/backup/opatch_apply_$(date +%Y%m%d).log看到OPatch succeeded.只代表二进制替换完成,不代表数据库已经升级到新版本。这个区别后面 4.3 会展开,也是新手最容易误解的地方。
apply 结束顺手确认一下补丁确实进了清单:
# -detail 会列出 patches 段落,能搜到补丁号就说明二进制层完成 $ORACLE_HOME/OPatch/opatch lsinventory -detail | grep -i 33477185 # 新版 OPatch 支持直接列补丁,老版本没这个子命令就用上面那条 $ORACLE_HOME/OPatch/opatch lspatches4.2 RAC 与 Data Guard:顺序错了要重来
RAC 的关键在于每个节点的 home 二进制是各自一份,哪怕目录看起来是共享存储,实际生效的库文件仍在各节点本地。所以每个节点都得打,用的是-local参数,表示只更新本节点、不碰中央清单里的其他节点记录。
# 在节点 1 执行,先确认本节点实例已停 $GRID_HOME/bin/srvctl status instance -d orcl -i orcl1 cd /u01/soft/psu/33477185/33477185 $ORACLE_HOME/OPatch/opatch apply -local节点 1 打完,换节点 2 做一遍同样的动作。全部节点打完之后,再回到任意一个节点执行一次 catbundle,注意只能执行一次,重复执行会报字典对象已存在。
Data Guard 的顺序我一般按“先备库、后主库”走。备库停掉 redo apply 不影响业务,先把备库打完,再打主库,两边补丁级别一致之后再恢复 redo 传输。反过来先打主库,主库产生的新 redo 备库应用时会报错,日志里一堆 ORA- 看着吓人,其实只是版本不匹配。
# 备库上先停日志应用,打完补丁再开 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'EOF' alter database recover managed standby database cancel; exit EOF如果业务不允许停机,可以考虑 Data Guard 的滚动升级路径:先 switchover 到备库,把旧主库打完补丁变成新备库,再切回来。这条路对操作熟练度要求高,第一次打这个补丁的环境不建议上。
4.3 catbundle.sql 与 utlrp.sql:真正的升级在这一步
opatch apply换的是二进制文件,数据库里的数据字典、组件版本还停在旧状态。11.2.0.4 靠catbundle.sql把字典更新到新版本,这一步必须在startup upgrade模式下跑,普通模式会直接报错退出。
$ORACLE_HOME/bin/sqlplus -s / as sysdba <<'EOF' -- 必须以 upgrade 模式启动,普通模式下 catbundle 会拒绝执行 startup upgrade -- psu apply 表示正向应用,回滚时用 psu rollback @?/rdbms/admin/catbundle.sql psu apply -- 重新编译失效对象,跑完这一条才算完整 @?/rdbms/admin/utlrp.sql -- 正常关闭再启动,让实例回到可用状态 shutdown immediate startup exit EOFcatbundle.sql会在$ORACLE_HOME/rdbms/log/下生成catbundle_psu_<SID>开头的日志文件,跑完一定要去看,别只看 sqlplus 屏幕上滚过的最后几行。日志里搜ERROR和ORA-,正常情况应该只有少量预期内的“对象已存在”类提示。
# 找日志并扫错误 ls -lt $ORACLE_HOME/rdbms/log/catbundle_psu_*.log | head -3 grep -nE "ORA-|ERROR" $ORACLE_HOME/rdbms/log/catbundle_psu_*.log | head -20utlrp.sql在大库上可能跑十几分钟到一两个小时,取决于失效对象数量。如果跑到一半报 ORA-04021(等待对象锁超时),通常是还有别的会话连着库,先把业务连接断干净再重跑。utlrp.sql可以重复执行,catbundle.sql psu apply不行,这个差别要记住。
4.4 打坏了怎么退:rollback 的两段式
补丁出问题要回滚,动作和安装严格对称,顺序反过来就行,但两段都不能少。
# 第一段:停库,回滚二进制 $ORACLE_HOME/bin/sqlplus -s / as sysdba <<'EOF' shutdown immediate exit EOF cd /u01/soft/psu/33477185/33477185 $ORACLE_HOME/OPatch/opatch rollback -id 33477185-- 第二段:upgrade 模式下回滚字典 startup upgrade @?/rdbms/admin/catbundle.sql psu rollback @?/rdbms/admin/utlrp.sql shutdown immediate startupRAC 环境同样是每个节点opatch rollback -local,最后在一个节点跑一次catbundle.sql psu rollback。只做第一段就起库,会看到 ORA-04063 之类的字典错误,那不是数据坏了,是字典还在新版本上。
5. 避坑:五个把库打回原形的常见问题
5.1 报“Patch 33477185 is not applicable to the target Oracle Home”
现象:命令回车后 opatch 在 prereq 阶段直接终止,连文件都没开始拷。
原因:执行的 home 不是 11.2.0.4 的 64 位数据库 home。常见情况有三种:在 32 位 home 上打、在 Grid Infrastructure home 上打(GI 有独立的 GI PSU,补丁号不同)、home 里已经装了更高版本的同类 PSU。
解决:opatch lsinventory -detail看输出头部的 home 信息和已装补丁列表。版本高于本补丁的,只能先 rollback 高版本再装,不能覆盖。GI home 用 GI 对应的补丁包,两个 home 的补丁要分别打。
5.2 补丁打上了,应用连上去还是旧行为
现象:lsinventory里能看到 33477185,registry$history里没有新记录,dba_registry的组件版本也没变。
原因:只做了opatch apply,没跑catbundle.sql。二进制换了,字典没换,数据库自己都不知道自己升级了。
解决:按 4.3 的流程补跑一遍 upgrade 模式下的 catbundle 和 utlrp。如果 apply 之后直接startup进了普通模式跑过业务,先停库再startup upgrade,顺序不能颠倒。
5.3 空间检查失败或中途“No space left on device”
现象:opatch prereq CheckSystemSpace返回失败,或者 apply 到 70% 报磁盘写满。
原因:.patch_storage要备份所有被替换的文件,占用常常是补丁本体的三到五倍;另一个高频原因是TMPDIR指向了只有几 G 的/tmp。
解决:df -h $ORACLE_HOME确认余量,不够就先清理归档或临时表空间文件。export TMPDIR=/u01/tmp指到大分区再执行。千万别在中途手工删.patch_storage,那是回滚的唯一依据,删了就只能重装 home。
5.4 RAC 只有一个节点打了补丁,另一个节点起不来
现象:节点 1 正常,节点 2 启动实例报 ORA-00600 内部错误,或者 ASM 起来后数据库实例反复重启。
原因:漏打节点。共享 ORACLE_HOME 看起来像一份,实际每个节点的二进制文件是各自安装的,两边版本不一致时,集群层面就会出现莫名其妙的内部错误。
解决:每个节点都执行opatch apply -local,全部完成后再在一个节点跑 catbundle。已经出现不一致的,把漏掉的节点补上,再重启整库,不用回滚。
5.5 回滚后库起不来,报字典相关错误
现象:opatch rollback成功,startup报 ORA-04063 或 ORA-00600。
原因:只回滚了二进制,没跑catbundle.sql psu rollback,字典还停在新版本上,二进制和字典对不上。
解决:先看registry$history最后一条记录确认当前状态,然后按 4.4 的两段式补完第二段。已经用新字典跑过业务数据的,回滚前务必确认这期间没有结构性变更,必要时用 3.2 的备份恢复。
6. 装完怎么验证:四条命令确认补丁真的生效
凌晨打完补丁最容易犯的错,是看到OPatch succeeded.就关掉终端。真正的验证要同时看二进制层和字典层,两边都对上才算完成。
# 1) 二进制层:清单里能搜到补丁号 $ORACLE_HOME/OPatch/opatch lsinventory -detail | grep -i 33477185-- 2) 字典层:最后一条记录应该是本次 PSU set lines 220 pages 200 col comments format a40 select to_char(action_time,'YYYY-MM-DD HH24:MI') t, action, namespace, version, id, bundle_series, comments from registry$history order by action_time; -- 3) 无效对象数量应该和打补丁前一致或更少 select count(*) invalid_objects from dba_objects where status = 'INVALID'; -- 4) 组件状态全部 VALID select comp_id, version, status from dba_registry where status <> 'VALID';四条命令对应四个验证点,缺一条都可能留下隐患:
| 验证项 | 命令 | 期望结果 |
|---|---|---|
| 二进制补丁 | opatch lsinventory -detail | 输出含 33477185 |
| 字典记录 | registry$history | 新增一条 PSU 应用记录 |
| 失效对象 | dba_objects统计 | 与 3.3 的基线持平或更少 |
| 组件状态 | dba_registry | 无返回行(全部 VALID) |
我还会多做一步:把打之前的lsinventory_before.txt和现在的清单 diff 一遍。
$ORACLE_HOME/OPatch/opatch lsinventory -detail > /u01/backup/lsinventory_after_$(date +%Y%m%d).txt diff /u01/backup/lsinventory_before_*.txt /u01/backup/lsinventory_after_*.txt | head -40diff 里应该只出现本次补丁相关的行。如果冒出一堆意料之外的组件变化,说明这个 home 上还有别的补丁动作,得先弄清楚再宣布完成。最后别忘业务侧回归:起监听、连一个真实业务账号、跑一条平时最慢的 SQL 看执行计划有没有变化,再翻一眼 alert 日志里有没有新的 ORA- 报错。
从那以后我每次打完 PSU,不管多晚,都强制走一遍“清单 diff + registry 对比 + 无效对象计数”这三步,少一步都觉得心里没底。希望帮到你。
本文还有配套的精品资源,点击获取