简介:面向Oracle数据库管理员与运维人员的11.2.0.1版本补丁安装参考文档,聚焦停机窗口内如何安全完成补丁部署与验证,适合需要处理遗留系统维护、补丁升级和故障排查的初中级DBA查阅。整份资料以单个doc文件交付,压缩包约942KB,篇幅紧凑,便于随取随用。内容围绕安装前实例关闭、数据与控制文件备份、补丁包获取与解压,到OPatch工具应用、按编号由小到大逐个执行并确认succeed,再到补丁清单核对、功能测试与维护记录展开;同时给出特殊情况的排查方向,如查看已装补丁、按补丁ID回滚及前置条件检查,帮助读者建立可复用的补丁管理思路。已有131人学习,适合作为一次补丁实施的操作备忘与检查清单。
1. 数据库补丁不是“敲一条命令”,先把退路留好再动手
线上巡检发现某个库还停在 11.2.0.1,一份 .doc 记着完整的动作序列:关实例、设ORACLE_HOME、进补丁解压目录、opatch apply、按数字从小到大一个个打、重启服务器、succeed即通过。这份资源就是这套流程的落地记录,解决的是“知道要打数据库补丁,但不确定命令在哪执行、顺序怎么排、打完怎么确认”的问题。适合手上有 11.2.0.1 实例、只能靠命令行加 OPatch 作业的 DBA 和运维,也适合刚接手一套老库、只敢照着文档敲的人。
省事的地方是命令确实少,麻烦的地方全在顺序和退路上。同一份文档,照对了能一次过,敲错一步就可能要靠备份兜底。所以第 1 章只强调一条:数据库补丁前后都要能在脑子里重放“失败怎么退”,能回退,才动手。
2. 补丁前的准备:实例状态、OPatch 版本、可回退备份
打补丁失败的原因,绝大多数不在opatch apply那一刻,而在动手前的三十分钟。这一章把准备拆成三件必须闭环的事,每一件都能单独拦住一次翻车。
2.1 关实例不是关窗口:确认数据库真的停了
很多“补丁打不上”的记录,往下追其实是实例还在跑。OPatch 要替换ORACLE_HOME下的二进制和库文件,Windows 上文件被进程占用会直接报错,Linux 上不报错但运行中的实例可能因为加载了旧库而行为异常。所以第一步不能只看窗口关了没有,要用 SQL*Plus 正常关闭。
-- 以 sysdba 身份连接,确认实例当前状态 sqlplus / as sysdba SQL> select instance_name, status from v$instance; -- 期望看到 OPEN,说明还在跑 SQL> shutdown immediate; -- immediate 会回滚未提交事务并断开连接,比 abort 干净 SQL> select status from v$instance; -- 这里应该报 ORA-01034,说明实例已经卸下shutdown immediate是常规选择,别图快用abort,一个没写盘的事务在补丁失败后想恢复会多花几倍时间。normal在生产上基本等不到,会话不掉就不关。关完实例还要确认监听,Windows 上顺手停掉服务,别让它中途拉起会话:
# Windows:确认 Oracle 服务已停止 sc query OracleServiceORCL net stop OracleServiceORCL # Linux:停监听,避免打补丁过程中有新连接进来 lsnrctl stop服务名里的ORCL是实例名,实际按现场来。有的现场实例名和库名不一致,OracleService<SID>里的 SID 要以select instance_name from v$instance为准,别照抄文档里的示例。停完用tasklist | findstr oracle或ps -ef | grep ora_再扫一眼,有残留后台进程就手动确认来源,别急着往下走。
提示:如果这套库配了 Data Guard 或还有别的节点,先把所有节点都停到位再动手,单节点打完再同步另一节点,会比边打边同步省很多解释成本。
2.2 OPatch 得先过版本关,用库里自带的那份常常不够
OPatch 本身是个可以独立升级的工具,它不是在库安装完就固定不变的。每个补丁的README.txt或README.html里都写着最低要求的 OPatch 版本,版本不够时opatch apply会先报一条前置检查失败,然后连补丁都不往下读。我一般先查当前版本,再对比 README 里的要求:
set ORACLE_HOME=D:\oracle\product\11.2.0\dbhome_1 %ORACLE_HOME%\OPATCH\opatch version # 输出形如 OPatch Version: 11.2.0.3.x # 再翻补丁目录下的 README,找 "OPatch Version" 一节比对版本低了就整份替换,别想着手工往里拷某几个 jar,那样后面大概率在opatch lsinventory阶段出更奇怪的错误。常见做法是拿 OPatch 的通用补丁包(多年来一直是同一个补丁号 6880880 对应各平台各版本),按平台下载后解压到临时目录,把ORACLE_HOME\OPATCH整个改名备份,再把新目录拷进去:
# 先备份旧的,出问题能整份换回来 move D:\oracle\product\11.2.0\dbhome_1\OPATCH D:\backup\OPATCH_old # 把新版 OPatch 解压到 ORACLE_HOME,解压后目录名就是 OPATCH # 解压完再验一次版本 %ORACLE_HOME%\OPATCH\opatch version替换前要注意,OPatch 目录里可能有.patch_storage的历史记录,新目录解压下来是干净的,旧记录跟着旧目录一起被挪走。这个行为本身没问题,但意味着你后面想opatch rollback到很早以前的补丁时,得先把旧目录换回来才能查到当时的库存信息。所以旧目录别急着删,压成一个包放在备份盘里。
2.3 备份清单:这几样少一样都别开始
补丁失败后的恢复路径有两条,一条是opatch rollback,一条是整库恢复。前者依赖ORACLE_HOME的补丁库存,后者依赖数据文件备份。两条路都要提前铺好,只备其中一条,遇到另一类故障就只能重装。
| 备份对象 | 位置 | 为什么必须备 |
|---|---|---|
| 数据文件 + 控制文件 | 数据盘 / RMAN 全备 | 补丁后跑脚本改数据字典,逻辑损坏只能靠它 |
| SPFILE 或 PFILE | %ORACLE_HOME%\database | 参数被误改时能快速回退 |
ORACLE_HOME整目录 | 文件系统快照或压缩拷贝 | 无法 rollback 时的最后一层兜底 |
OPATCH目录 | 同上 | 保留历史.patch_storage,回滚要用 |
监听配置listener.ora/tnsnames.ora | %ORACLE_HOME%\network\admin | 补丁期间有人改过配置就能对比 |
整目录备份在 Windows 上比较笨重,实际更多用虚拟机快照或存储侧快照代替,效果一样:出事直接回滚到快照点,比文件拷贝快得多。没有快照条件的小环境,至少把ORACLE_HOME压一份到别的盘,压缩前确认实例已停,不然会拷到一半文件的状态不一致。
备份做完别只写一句“已备份”,把路径、时间、大小记在一张纸上或工单里。我见过备份做得很足、出事时找不到放在哪台机器上的情况,这个成本比不做备份还高。另外,补丁压缩包本身也要留着,这一条资源里专门写了——补丁打完,解压出来的文件夹可以删,压缩包别删,后面写文档、对照版本、复现问题都得靠它。
3. 单补丁 apply 实操:ORACLE_HOME、执行目录、日志三件套
准备做完,正式进入opatch apply。这一章把一条命令拆成三个变量:环境变量指向哪个ORACLE_HOME、命令在哪个目录执行、执行完去哪里看结果。三个变量错一个,命令要么报错,要么“看起来成功但没生效”。
3.1 为什么必须切到补丁解压目录再执行
opatch apply不带路径参数时,默认把当前工作目录当作补丁位置,去里面找etc/config/inventory.xml和README.txt。所以资源里那句“打开数字补丁包所在的路径,执行命令”,不是习惯问题,是命令语义决定的。如果你站在%ORACLE_HOME%\OPATCH目录里敲opatch apply,它会跑去找 OPatch 自己的目录当补丁,直接报“不是有效的补丁位置”。
正确顺序是先进补丁目录,再设ORACLE_HOME,最后用全路径调 OPatch:
# 1. 设置 ORACLE_HOME,指向要打补丁的那套库 set ORACLE_HOME=D:\oracle\product\11.2.0\dbhome_1 # 2. 切到补丁解压目录(目录名通常就是补丁号) cd D:\patches\17082367 # 3. 用全路径调用 OPatch,避免 PATH 里混进别的版本 %ORACLE_HOME%\OPATCH\opatch apply第 3 步用全路径而不是直接敲opatch,是为了避开一个很隐蔽的坑:机器上装过多套库或者换过 OPatch 版本时,PATH 里指向的可能是另一个ORACLE_HOME下的 OPatch,跑完看着成功,结果补丁进错了库。用绝对路径调用,再把ORACLE_HOME显式设成目标库,两个变量同时钉死,基本不会串。
一个ORACLE_HOME下有多套库实例(多实例共用一个 home)的情况,ORACLE_HOME设一次就行,但要把该 home 下的所有实例都停掉,不能只停一个。补丁改的是共享的二进制文件,停一个实例不影响另一个实例继续占用这些文件,后面照样报占用。
3.2 opatch apply 的交互确认、静默与参数
命令敲下去,OPatch 会先做一轮前置检查:版本、空间、冲突、ORACLE_HOME完整性,然后列出这个补丁会影响哪些组件,最后问一句Do you want to proceed? [y|n]。这一步别盲敲y,先看上面那段列表:组件名对不对、当前版本号对不对、有没有报Warning。
# 交互式执行,逐项确认后输入 y %ORACLE_HOME%\OPATCH\opatch apply # 脚本化/无人值守场景用 -silent,跳过所有交互确认 %ORACLE_HOME%\OPATCH\opatch apply -silent # 只打当前节点,RAC 环境下逐节点操作时用 %ORACLE_HOME%\OPATCH\opatch apply -local # 动手前单独跑冲突检查,不改文件 %ORACLE_HOME%\OPATCH\opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir D:\patches\17082367参数的选择逻辑:单人维护、补丁数量不多,老老实实交互式,出错时能第一时间看到提示;补丁成批打、环境已在工单里锁死,用-silent加日志重定向,把输出全量落到文件里。-local在 RAC 场景下很有用,但它只处理当前节点的ORACLE_HOME,库存信息也是节点本地的,别以为打完一个节点另一个节点就自动同步了。
-phBaseDir指向补丁目录(不是子目录),配合opatch prereq可以在真正改文件之前把所有前置条件过一遍。这一步花两分钟,能省掉一次“打到一半失败、回滚、重新来”的半小时。
注意:
-silent不会降低检查强度,只是把交互问题默认按继续处理。前置检查失败时它照样会停,退出码非 0,脚本里要判退出码,别只看有没有输出succeed。
3.3 看到 succeed 之后还要读的三处日志
OPatch succeeded.是结论,不是证据。真正能证明这次补丁干净落地的,是三处输出:命令行回显里的组件版本变化、lsinventory的库存记录、以及 OPatch 自己写的日志文件。
# 1. 查库存,看补丁有没有登记进去 %ORACLE_HOME%\OPATCH\opatch lsinventory -detail # 2. 单独列出已安装补丁号,输出更短,适合粘贴进工单 %ORACLE_HOME%\OPATCH\opatch lspatches # 3. 日志默认落在这些位置,失败时第一优先看这里 # %ORACLE_HOME%\cfgtoollogs\opatch\opatch<时间戳>.log # %ORACLE_HOME%\.patch_storage\<补丁号>\ 下的执行记录lsinventory -detail的输出里,重点看两段:Oracle Home和Inventory的路径是不是你预期的那个 home,以及Patches installed in the Oracle Home下面有没有刚打的补丁号。如果补丁号不在里面,哪怕命令行回了succeed,也说明它没进这套库的库存,大概率是ORACLE_HOME指错了。
日志文件最容易在失败时被忽略。opatch apply报一个笼统的失败码,真正的根因往往写在日志最后二十行,比如某个文件权限不够、某个前置补丁缺失、磁盘空间不足。养成“命令回显看结论、日志看根因”的习惯,排错时间能砍掉一大半。
4. 多个数字补丁的顺序:从小到大、逐个目录、逐次 apply
资源里有一句关键描述:“数字补丁包从数字小的打到大的”。这句话不是经验,是补丁之间的依赖关系决定的。这一章讲清顺序为什么不能乱,以及多个补丁怎么打得不重不漏。
4.1 顺序乱了的代价:冲突检查和补丁依赖
Oracle 的补丁分几类:一次性小补丁(one-off)、累积补丁(CPU/PSU)、以及合并补丁(merge patch)。后打的补丁如果构建时已经把前面的补丁内容包含进去,就叫 superset 关系,此时先打大的再打小的,小的会因为文件版本已被覆盖而报冲突,或者反过来把大补丁覆盖掉一部分,形成一个谁也说不清的中间态。
所以排序原则很朴素:按构建时间先后,通常就对应补丁号从小到大。资源里“数字小的先打”正是这个意思。判断顺序不要凭补丁号大小拍脑袋,两个补丁号相近但属于不同产品线时大小没有可比性,最终还是看每个补丁 README 里的Prerequisites和Supersedes两节:前者说它依赖谁,后者说它包含了谁。
# 打之前先做冲突检查,输入是补丁目录 %ORACLE_HOME%\OPATCH\opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir D:\patches\17082367 # 输出里出现 "Conflicts with the current OH" 就停下来看清楚冲突对象冲突检查建议每个补丁各跑一次,别把几个补丁目录塞进同一个父目录一起检查。塞在一起时 OPatch 会把它们当成一个合并补丁集处理,冲突结论也跟着变,反而不准。
4.2 手工逐个打与批处理循环
补丁数量三五个,手工逐个打最稳:进目录、apply、看succeed、lspatches确认、再进下一个。数量上到十几个,就该用脚本把重复动作固定下来,避免漏打或者顺序打乱。
@echo off set ORACLE_HOME=D:\oracle\product\11.2.0\dbhome_1 set OPATCH=%ORACLE_HOME%\OPATCH\opatch rem 按补丁号从小到大显式列出,不要用 for /d 自动遍历 rem 自动遍历的字典序会把 999999 排在 1000000 前面,顺序就错了 for %%P in (17082367 17272546 17478514) do ( echo ===== applying %%P ===== pushd D:\patches\%%P "%OPATCH%" apply -silent if errorlevel 1 ( echo FAILED at %%P, stop here popd exit /b 1 ) popd ) echo ALL PATCHES APPLIED这段脚本有两个设计点值得说。第一,补丁号是手写枚举的,不用目录遍历,因为目录名按字符串排序时1000000会排在999999前面,数字顺序完全乱掉,这个坑在补丁号跨位数时必现。第二,errorlevel判断后直接exit /b 1,一旦某个补丁失败就整体停下,不继续往后打。继续往后打的后果是后面的补丁建立在一个不完整的基础上,最后要么全部回滚,要么带着一个隐形的版本缺口上线。
Linux 环境下同样的逻辑用 shell 写,注意opatch必须用 oracle 用户执行,不要用 root,root 跑完会在ORACLE_HOME下留一堆属主错误的文件,后面实例起不来:
export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 for p in 17082367 17272546 17478514; do echo "===== applying $p =====" cd /u01/patches/$p || exit 1 $ORACLE_HOME/OPATCH/opatch apply -silent || { echo "FAILED at $p"; exit 1; } done4.3 中断在第 N 个补丁时的止损动作
批处理跑到一半失败,最忌讳的动作是“修一下继续往后打”。正确顺序是:先看失败补丁的日志定位根因,再决定是回滚已打的补丁还是只回滚当前这一个,最后重新从失败的那个补丁开始往后打。
# 看当前库存,确认前面几个补丁已经进去了 %ORACLE_HOME%\OPATCH\opatch lspatches # 只回滚当前失败的补丁 %ORACLE_HOME%\OPATCH\opatch rollback -id 17272546 # 回滚完再验一次库存,确认干净了再重新 apply %ORACLE_HOME%\OPATCH\opatch lspatches判断要不要连带回滚前面成功的补丁,看失败补丁的 README 里有没有写“must be applied on top of X”。如果它明确依赖前一个补丁,前一个就别动,只回滚当前这个,修完重打。如果几个补丁是并列关系、互不依赖,也只是当前这个失败,前面成功的保持不动即可。
还有一种情况是apply中途被杀掉(断电、误关窗口),此时库存可能处于半登记状态。这时候不要反复重跑apply,先跑opatch lsinventory看这个补丁号在不在列表里:在,说明登记完成只是文件没拷全,需要 rollback 后重打;不在,说明还没开始改文件,直接重打即可。这个判断能避免把半成品状态越搞越乱。
5. 避坑与排查:五条踩过的坑,每条都有解
补丁流程本身不复杂,复杂的是现场环境。这一章记录五条实际踩过、且每次都长记性的问题,按“现象 → 原因 → 解决”写,遇到时可以直接对照。
5.1 报 OPatch failed with error code 73,反复重试还是 73
现象:opatch apply跑到一半退出,末尾一行OPatch failed with error code 73,往上翻只有几行组件列表,看不出具体哪里错。
原因:73 是 OPatch 的通用失败码,本身不携带信息,真正的根因在它上面那段Prerequisite check或者日志文件里,常见的是空间不足、OPatch 版本过低、ORACLE_HOME库存不一致三类。
解决:别重试,先看%ORACLE_HOME%\cfgtoollogs\opatch\下最新的日志,搜Error和failed关键字;再用opatch prereq CheckSystemSpace -phBaseDir <补丁目录>单独查空间,用opatch version对一遍 README 要求的最低版本。三个里排掉两个,剩下的就是根因。
5.2 打完补丁没跑 SQL 脚本,组件版本还是旧的
现象:opatch apply回了succeed,lspatches里也能看到补丁号,但dba_registry里查组件版本,还是补丁前的旧值。
原因:PSU/CPU 这类累积补丁分两步,OPatch 只负责替换二进制文件,数据字典的更新要靠补丁目录里带的 SQL 脚本。11.2.0.1 上常见的是catbundle.sql,不跑这一步,库能起来,但一部分修复在数据库层没生效。
解决:补丁 README 里会写明“Post Installation”一节,按它执行,通常在实例启动到OPEN之后、以 sysdba 身份跑:
-- 实例启动后执行,具体脚本名以补丁 README 为准 sqlplus / as sysdba SQL> @?/rdbms/admin/catbundle.sql psu apply -- 跑完再查组件版本,确认已更新 SQL> select comp_name, version, status from dba_registry;这一步跑完记得重新编译失效对象,utlrp.sql走一遍,否则应用侧可能报一堆ORA-04063。
5.3 多个补丁包塞在同一个目录,apply 报找不到补丁信息
现象:把几个补丁解压到同一个父目录下,进父目录执行opatch apply,报Unable to find 'etc/config/inventory.xml'或提示不是有效的补丁位置。
原因:opatch apply只认“当前目录就是一个补丁根目录”,不会往下递归找。几个补丁解压在一起,父目录本身没有补丁元数据,自然报错。
解决:一个补丁一个目录,目录名就用补丁号,父目录只做分类用途,执行时cd到具体补丁号那一层。这条看着像常识,但批量解压时用“解压到当前文件夹”很容易把几个包的内容混在一起,事后分不清哪个文件属于哪个补丁。
5.4 Windows 下文件被占用,apply 半途失败
现象:apply跑到拷贝文件阶段报Unable to remove ... being used by another process,重试仍在同一个文件上失败。
原因:实例没停干净,或者有别的进程(备份代理、监控采集、手工开的 sqlplus 会话)持有ORACLE_HOME下的文件句柄。
解决:先用tasklist找oracle.exe相关进程,逐个确认来源再结束;停掉监听和数据库服务;如果有备份代理在跑,临时停掉它的策略。确认没有句柄占用再重跑apply,别在文件被占用时反复重试,重试只会让半拷贝状态的文件越来越多。
5.5 只备了数据文件,回滚时发现回不去
现象:补丁打完后实例起不来,想回滚发现opatch rollback报库存里找不到补丁记录,想整库恢复又只有数据文件备份,ORACLE_HOME已经是坏状态。
原因:备份策略只覆盖了数据层,没覆盖软件层。rollback依赖ORACLE_HOME的.patch_storage和 OPatch 库存,这两样在替换 OPatch 或手工删目录时容易丢。
解决:备份时把ORACLE_HOME一起纳入,虚拟机环境直接打快照最省事。已经出问题的,先用opatch lsinventory确认库存状态,库存还在就rollback -id,库存丢了就只能整目录恢复再重建实例,这个代价会明显高出一截。
6. 验证与回滚:lsinventory、rollback 和一份补丁台账
补丁打完、服务器重启、实例自动起来,很多人到这一步就收工了。真正决定这套流程能不能复用的,是后面三件事:库存读得懂、回滚敢执行、台账留得下。
opatch lsinventory -detail的输出值得完整读一遍,不只找补丁号。开头几行是Oracle Home和Central Inventory的路径,确认它们指向你这次操作的那套库;中间Active Nodes在单机环境只有一个主机名,在 RAC 下要能看到全部节点;Patches installed in the Oracle Home下面每个补丁号后面跟着Applied on日期和Description,日期对不上说明这套库被人动过,得先问清楚。opatch lspatches是同一份信息的精简版,适合贴进工单和变更记录,两个命令配合用:先lspatches拿清单,再lsinventory -detail核路径。
回滚的边界要提前想清楚,别等出事再研究。opatch rollback -id <补丁号>的前提是这套ORACLE_HOME的库存里还留着这个补丁的记录,且之后的补丁没有覆盖它。多个补丁连续打的情况下,回滚要从后往前,先回滚最后一个,再回滚前一个,顺序反了会因为文件版本不匹配失败。如果中间穿插过catbundle.sql这类改数据字典的脚本,光回滚二进制不够,数据字典那部分往往没有对称的回退脚本,这时候整库恢复才是唯一干净的路。所以每次动手前问自己一句:这次操作如果只能整库恢复,备份够不够新。
台账我一般记四列,不多但够用:
| 字段 | 记录内容 | 用途 |
|---|---|---|
| 补丁号 | OPatch 输出里的数字编号 | 排查时唯一标识 |
| 应用时间 | 精确到分钟 | 和应用侧故障时间对齐 |
| 执行人 | 实际操作账号 | 出问题能找到当事人问上下文 |
| 回滚方式 | 可 rollback / 仅整库恢复 | 决定应急时的处置路径 |
有一回我在测试环境连着打了三个补丁,中间那个失败后图省事直接重跑,结果库存里登记了两次同名补丁,后面lsinventory的输出多出一段看不懂的重复记录,花了半天才理顺。从那以后我每次apply之前都强制走一遍lspatches截图存档,打完再截一次,两次对比就是这次变更最直接的证据,比事后回忆靠谱得多。需要原始 .doc 对照命令的,直接取用那份文档就行,压缩包也建议按它说的留着,解压目录可以清。希望帮到你。
本文还有配套的精品资源,点击获取