1. PSU补丁到底在修什么:为什么说“单库环境也不能跳过”
先聊一个很多人都会问的问题:单库环境,不搭建RAC,也不做Data Guard,就一个孤零零的数据库实例在跑,是不是就可以不打补丁了?我的答案很直接——恰恰相反,单库环境才是最需要认真对待补丁的场景。RAC集群节点多,出了问题还能挨个滚动处理,单库环境一旦因为一个已知Bug挂掉,那就是全站不可用,连个托底的节点都没有。
Oracle 11g这些年积累的PSU(Patch Set Update,补丁集合更新)数量非常多,类别集中在几块:一是安全漏洞修复,每个月Oracle都会发布关键安全补丁公告,涉及数据库本身的权限绕过、缓冲区溢出、SQL注入类风险,这些都是能被人直接利用的;二是高概率触发的内部错误,比如ORA-600、ORA-7445这些内部错误,很多修复被合并进PSU;三是SQL执行计划相关的Bug,尤其是11.2.0.4这个版本,优化器在特定场景下会选错执行计划,补丁里包含大量这类修复。
11g目前推荐的最终补丁基线是11.2.0.4.201020(也就是2020年10月的PSU),这也是Oracle对11g做过完整回归测试的最后一批补丁之一。如果是比较早期装的11.2.0.1或11.2.0.2,强烈建议先把数据库版本升到11.2.0.4,再在这个基础上打PSU,而不是在旧版本上跨越多个PSU逐个累积。为什么呢?因为Oracle对PSU补丁本身也是基于基线版本设计的,11.2.0.1上能用的PSU最早只到某一个版本,后续的PSU很多要求基线版本是11.2.0.4,你如果一开始就在低版本上打补丁,后面想升级基线版本时会变得异常痛苦,需要先回滚补丁再升级软件,这个过程在停机窗口内完成会很紧张。
单库环境下,PSU安装涉及三个核心组件:数据库软件本身(Oracle Home)、数据库实例(启动到mount状态即可,不需要完全打开)、以及监听器(必须完全停止)。这跟RAC环境最大的区别在于,RAC打补丁时通常采用rolling方式,一个节点一个节点地来,而单库环境没有这个条件,必须一次性对所有组件完成补丁操作,中间任何一步出错,影响范围都是整个数据库。后面我会详细讲每一步怎么操作,以及哪些环节最容易翻车。
2. 动手前的准备清单:OPatch版本、环境变量和备份策略
PSU安装最怕的不是补丁本身有问题,而是环境准备不到位。我见过太多人在打补丁中途卡住,回头一看,要么是OPatch版本太旧,要么是ORACLE_HOME环境变量指向了错误的路径,要么是备份没做全就急着停库。这个阶段多花半小时,后面就能少熬一个通宵。
2.1 OPatch工具版本检查与升级
先强调一个关键点:OPatch工具版本必须和Oracle版本、补丁版本都匹配,缺一个都不行。11.2.0.4版本的数据库,OPatch最低要求一般是11.2.0.3.6以上,实际经验是越新越好,推荐使用11.2.0.3.17或更高版本。如果OPatch版本太老,打补丁时会出现类似"OPatch version ... cannot be applied to ..."这样的报错。
检查当前OPatch版本的命令:
$ORACLE_HOME/OPatch/opatch version如果版本偏低,需要从Oracle支持网站下载对应版本的OPatch工具压缩包,然后解压替换到ORACLE_HOME/OPatch目录下。替换之前先备份原有的OPatch目录:
mv $ORACLE_HOME/OPatch $ORACLE_HOME/OPatch_bak unzip p6880880_112000_Linux-x86-64.zip -d $ORACLE_HOME替换完成后再次执行opatch version确认版本号已经变化。这一步看着简单,实际上很多人漏了,等到opatch apply执行到一半才提示版本不支持,那时候再停下替换就麻烦了,因为补丁过程可能已经修改了部分文件,状态会变得很微妙。所以OPatch版本检查务必在执行一切操作之前先做。
2.2 环境变量与依赖库检查
数据库软件安装时,环境变量很容易因为多次切换用户、多个Oracle环境共存而变得混乱。打补丁前,建议以oracle用户登录,重新确认以下环境变量:
echo $ORACLE_HOME echo $ORACLE_SID which sqlplus which lsnrctl确保sqlplus和lsnrctl指向的都是$ORACLE_HOME/bin目录下的程序,而不是系统默认路径下的同名文件。这个坑我踩过,之前有台机器上which sqlplus指向了/usr/bin/sqlplus,实际上是一个软链接,查了一圈才发现问题出在这个地方。
同时检查操作系统层面的必要依赖,比如libaio、glibc等,不过11.2.0.4版本的依赖一般没问题,重点检查的是unzip命令是否可用,补丁包解压需要它。
2.3 备份策略:软件备份与数据备份
PSU补丁安装理论上不涉及数据文件的内容,但做不做备份完全是两个概念。我建议至少备份两个层面:
第一层是Oracle Home的备份。可以用简单的tar打包整个$ORACLE_HOME,排除数据文件目录和监听日志这类动态文件。命令参考:
tar -zcf /u01/backup/oracle_home_bak_$(date +%Y%m%d).tar.gz --exclude=$ORACLE_HOME/dbs --exclude=$ORACLE_HOME/network/log $ORACLE_HOME注意$ORACLE_HOME/dbs目录虽然要备份,但一般很小,主要包含参数文件和密码文件,这个不能排除,我说的是排除数据目录,不是参数目录。上面的tar命令中--exclude参数只排除了警戒日志和大文件目录,其他都要完整打包。这个打包过程可能需要几分钟,取决于Oracle Home的大小。
第二层是数据库层面的备份。对于非归档模式,最简单的方式是用RMAN做一次一致性备份,或者用数据泵导出全部用户数据。更简单的方案是直接把数据文件、控制文件、日志文件的目录用tar复制一份,前提是数据库处于正常关闭状态。打补丁时数据库本来就要停,所以做冷备非常方便,占用额外的时间也不多。
这里还得提一个容易被忽略的对象:$ORACLE_HOME/dbs目录下的spfile或pfile,以及orapw密码文件。这两个文件在补丁过程中不应该被修改,但万一出现异常,恢复时需要用它重建环境,打包时务必确认它们被包含在内。
2.4 补丁下载与解压
PSU补丁包在Oracle支持网站上下载,11g的PSU补丁包命名一般以p2xxxxxxx_112040_Linux-x86-64.zip形式出现,数字部分是补丁编号。下载时注意选择与操作系统平台匹配的补丁包版本,Linux x86-64是最常见的。
下载完成后,把zip包拷贝到一个独立的补丁目录,比如/u01/patch/,然后解压:
mkdir -p /u01/patch unzip p2xxxxxxx_112040_Linux-x86-64.zip -d /u01/patch解压后会生成一个以补丁号命名的目录,里面包含etc、files等子目录,以及最重要的README.txt。强烈建议先把README通读一遍,里面记录了该PSU补丁的适用范围、前置条件、已知问题。这个习惯我一直保留到现在,因为补丁包之间的差异不小,有的补丁要求必须安装特定的OJVM补丁配套,有的补丁则明确要求不能跟某个已知补丁共存,这些信息全在README里。
3. 环境预检这一步千万别省:把补丁冲突和空间问题提前排查掉
很多刚接触补丁安装的DBA,拿到补丁包就直接opatch apply,结果要么遇到冲突报错,要么因为空间不足导致解压失败。预检是可以在几分钟内完成的,却能避免后面完全不可控的状态。
3.1 opatch prereq检查与冲突检测
进入补丁目录后,先执行opatch prereq来做预检:
cd /u01/patch/2xxxxxxx $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./这个命令会检查补丁与当前Oracle Home中已安装的补丁是否存在冲突。如果输出中提示Conflict相关关键字,说明当前环境中存在与该PSU冲突的补丁或一次性补丁(One-Off Patch)。遇到冲突时,需要逐一确认冲突的补丁是否已经被合并进当前PSU。如果确实已被合并,可以卸载旧的补丁再应用,或者联系Oracle支持确认处理方案。
opatch apply执行前还会做一次自动冲突检测,比prereq检查更彻底,包括对库内对象、数据字典内容的预检。这时候如果检测到未处理的冲突,会直接终止补丁流程。所以先手动做一次prereq,心里有底。
3.2 空间检查:$ORACLE_HOME和/tmp目录双保险
补丁包解压和安装过程中,会往$ORACLE_HOME目录和临时目录写入大量文件,空间不足是补丁安装失败的常见原因之一。检查命令:
df -h $ORACLE_HOME df -h /tmp$ORACLE_HOME所在分区建议至少有5-10GB空闲空间,/tmp分区至少2GB以上。如果空闲空间不足,可以通过清理$ORACLE_HOME下的trace文件、日志文件来腾出空间。有一种做法是把/tmp空间映射到大分区目录,比如修改TMP和TMPDIR环境变量,指向一个有足够空间的目录,然后再执行opatch apply,这个做法可行但比较少见,我一般还是建议直接把空间腾够。
另一个容易忽略的点是,opatch apply过程中会用$ORACLE_HOME/.opatch目录存放临时文件和日志,如果这个目录所在分区空间紧张,同样会出问题,所以不要只盯着df -h /tmp看,还要看df -h $ORACLE_HOME。
3.3 数据库前的最后确认:关闭数据库并停止监听
预检全部通过后,就可以正式停库了。顺序上有讲究:先停监听,再关数据库实例。
lsnrctl stop sqlplus / as sysdba SQL> shutdown immediate; SQL> exit如果数据库中有活跃的会话,shutdown immediate会在完成当前事务后切断连接,整个过程可能持续几分钟。对于单库环境,如果应用无法及时断开,有可能导致shutdown卡住,这时候需要联系应用侧确认会话清理情况,必要时使用shutdown abort再执行startup做一次干净关闭。
补充说明:PSU补丁中有一个组件叫DBMS_DB_VERSION或catbundle.sql,这些SQL脚本是在数据库启动到UPGRADE或NORMAL状态后执行的,但注意补丁应用阶段Oracle官方文档一般建议数据库处于完全关闭状态,然后加-all参数让opatch完成二进制部分。规范流程是补丁二进制部分完成后,再启动数据库到相应状态执行SQL脚本部分(如果补丁包含SQL部分)。11g的PSU补丁,phantom进程会在opatch apply结束时提醒你后续需要执行catbundle.sql等脚本。这里不展开,后面讲到SQL apply阶段再细化。
停库后建议再确认一下没有任何后台进程还在运行:
ps -ef | grep ora_ | grep -v grep如果有进程残留,很可能是某个会话没有被彻底关闭。清理干净后,进行下一步。
4. PSU补丁安装主过程:opatch apply以及那些必须盯着的关键输出
准备工作做扎实之后,补丁安装本身其实就是一个命令的事,但前提是每一步都要盯紧输出,不能执行完命令拍拍屁股就走。opatch apply的过程里哪些输出是正常的,哪些是异常的,这里面门道不少。
4.1 执行opatch apply的正确姿势
在补丁目录下执行:
cd /u01/patch/2xxxxxxx $ORACLE_HOME/OPatch/opatch applyOPatch会自动检测Oracle Home的环境,读取补丁的元数据,然后提示你确认是否应用该补丁。输入y确认后,整个过程会持续几分钟到十几分钟。
过程中有几个关键输出点需要特别留意:
第一,Oracle Home Inventory的检测。OPatch会读取安装清单,如果清单信息不完整,会报出警告:Inventory is not consistent,这通常是因为之前某个一次性补丁的卸载记录不完整,或者oraInventory目录被改动过。遇到这个警告时,不要继续,先中止流程,用opatch lsinventory -detail查看清单,确认哪些补丁的记录存在异常,必要时手工修复清单,或者联系Oracle支持协助处理。
第二,Conflict Detection阶段如果出现红色Warning或者Error,务必高度重视。补丁冲突不像文件覆盖失败一样会立刻影响数据库,但它会在未来某个时刻引发莫名的问题,比如两个补丁修改了同一个库内对象,导致升级脚本执行失败。所以一旦看到冲突相关信息,宁可停下来处理掉,也不要带着冲突继续。
第三,Making PHP files ...阶段会看到类似于Generate the php files的输出,这表示OPatch正在把二进制文件复制到Oracle Home对应的目录结构中,这一步耗时最长,耐心等就是。
4.2 安装成功标志与日志核查
当命令执行完毕,最后几行输出大概是这样:
OPatch succeeded.看到这个基本大功告成,但不要急着收工。建议主动检查一下opatch apply的执行日志,默认位置是$ORACLE_HOME/.opatch/opatchYYYY-MM-DD_HH-MM-SS.log,用文本编辑器打开,搜索关键词WARNING和ERROR,逐条确认。有些WARNING是正常的,比如提示某个可选的目录不存在,但ERROR绝不能放过。
同时用opatch lsinventory再次确认补丁状态:
$ORACLE_HOME/OPatch/opatch lsinventory | grep -i "patch"看到你刚才安装的PSU补丁号出现在输出列表中,就说明补丁注册成功。
4.3 这里必须停一下:更新于数据库内的字典信息
11g的PSU补丁很多包含数据字典更新。如果你打完补丁直接startup就丢给应用使用,很可能在后续使用中遇到ORA-04063或者包状态被置为INVALID的问题。因此打完二进制补丁后,需要启动数据库,然后调用后续脚本。
标准流程是:启动数据库到open状态,然后执行:
sqlplus / as sysdba SQL> startup; SQL> alter pluggable database all open; -- 非CDB环境跳过接着执行PSU补丁里要求的SQL脚本。11g的PSU一般在$ORACLE_HOME/rdbms/admin/目录下提供了catbundle.sql脚本,补丁的README会明确说明需要执行的脚本名和路径。11.2.0.4的PSU常见的是:
cd $ORACLE_HOME/rdbms/admin sqlplus / as sysdba SQL> @catbundle.sql psu apply执行过程会输出大量PL/SQL过程、触发器的编译信息,这一步可能会持续一段时间,取决于数据库中的对象数量。脚本执行完成后,可以用以下命令检查无效对象:
SELECT COUNT(*) FROM dba_objects WHERE status='INVALID';如果有无效对象出现,特别是与SYS相关的包,需要重新编译:
@?/rdbms/admin/utlrp.sql这一步很多人觉得麻烦就跳过了,但跳过的后果是应用连接时出现奇怪错误,排查起来更费劲。
4.4 重启数据库确认状态
SQL脚本执行完毕后,建议再做一次干净的重启,让数据库加载所有新编译的对象:
sqlplus / as sysdba SQL> shutdown immediate; SQL> startup; SQL> select status from v$instance;看到OPEN状态,再启动监听:
lsnrctl start然后尝试连接数据库:
sqlplus system/xxx@localhost:1521/ORCL能正常连接,说明基本成功了。
5. 打补丁路上的经典翻车现场:监听起不来、包状态被丢弃、等保命令受影响
补丁安装完只是过了第一关,很多问题其实是在补丁装完之后才暴露出来的。我挑几个有代表性的问题场景,结合排查思路讲一下,方便大家在遇到类似现象时有个参考方向。
5.1 补丁装完后监听服务无法启动
有段时间我们做完PSU补丁后,执行lsnrctl start,输出卡在TNS-12541: No listener或者直接提示监听启动失败。当时第一反应是端口被占用,检查下来端口没被占,报错信息也模棱两可。后来发现是监听配置文件listener.ora里的LISTENER条目中引用的路径指向了旧的Oracle Home。
这是什么原因造成的?PSU补丁安装过程会把Oracle Home的路径记录在配置文件中,如果之前的ORACLE_HOME路径做过软链接调整,或者补丁包解压时意外覆盖了network/admin目录下的文件,就会导致路径不匹配。排查方式:
lsnrctl status lsnrctl start tail -f $ORACLE_HOME/network/log/listener.log如果日志里明确提示某个配置文件不存在或路径错误,先对比listener.ora里的HOST、PORT、PROTOCOL参数,再确认tnsnames.ora中的服务名是否与数据库的SERVICE_NAME匹配。
更麻烦的一种情况是监听服务的系统服务项在补丁后被禁用了,特别是用dbca注册过系统服务或防火墙规则只在安装时开放过的场景。遇到这种,用systemctl(Linux 7+)或service命令检查监听服务状态,重新注册服务:
cd $ORACLE_HOME/network $ORACLE_HOME/bin/netca /silent /responsefile $ORACLE_HOME/network/install/netca_typ.ora这一步可以重新生成监听配置和服务条目,一般能解决问题。
5.2 数据库包状态被置为“被丢弃”或“INVALID”
打完PSU后,应用连接时报出ORA-04068、ORA-04063这种错误,或者查看dba_objects时发现一大堆包状态是INVALID,这个场景非常经典。原因很简单:PSU提供的SQL脚本会更新一些核心包的定义,比如DBMS_STATS、DBMS_SQL、DBMS_UTILITY,如果脚本执行不完整,或者数据库中有第三方包引用了这些核心包且依赖关系没有被正确处理,就很容易出现对象失效。
还有一部分情况是,catbundle.sql执行时没有以SYS用户跑,或者当前会话的CURRENT_SCHEMA不对,导致脚本执行了一部分就报错终止,留了一半的更新在库里。
处理方法其实不复杂:
SQL> @?/rdbms/admin/utlrp.sql这个脚本会编译所有失效对象,执行完后再查一次无效对象数量。如果仍然存在无效对象,再单独查看是哪几个对象:
SELECT owner, object_name, object_type FROM dba_objects WHERE status='INVALID';针对具体的包,如果是应用自己的包,让应用侧重新编译发布;如果是SYS内置包,可能需要重新执行相关PSU脚本。这里要提醒一个细节:utlrp.sql执行完毕后,建议再重启一次数据库并重新查看dba_objects,确保所有对象在重启后依然是VALID状态。我遇到过一次utlrp.sql跑完显示全部编译成功,隔天数据库重启后又冒出一批无效对象的情况,最后发现是底层对象依赖顺序的问题,重新执行utlrp.sql并在之前先以startup upgrade方式启动数据库解决的。
5.3 等保命令与审计类查询受到补丁影响
关于等保合规检查中常用的一些命令,比如审计开启状态的查询、参数检查等,11g的PSU补丁一般不会修改这些功能,因为安全补丁更多是修漏洞、补内部校验,但在某些场景下需要注意:补丁之后数据库版本可能发生了变化,等保要求中需要定期扫描的漏洞列表可能会因为补丁版本更新而发生变化,因此补丁安装后的漏洞扫描结果要和之前做对比。假设你之前的安全扫描报告显示了某几个漏洞编号,打完PSU后扫描结果应该显示这些漏洞已被修复或不再出现。如果仍然报出同样的漏洞,要确认补丁是否生效,以及是否有额外的OJVM补丁需要一并安装。
这里顺带提个建议:如果你所在的单位有等保要求,建议把补丁安装日期、补丁号、操作系统版本、数据库版本等信息记录在变更记录中,方便后续的安全审计追溯。这虽然不是技术操作,但在实际项目验收时价值很大。
6. 补丁回滚与常见遗留问题的处理思路
万一补丁装完发现某条SQL执行计划变差了、某个功能跟应用代码不兼容,需要在停库窗口内把补丁回滚掉。回滚并不少见,下面说一下正确姿势。
6.1 回滚前必须知道的事:先确认能够回滚,而不是盲目卸载
opatch rollback的前提是补丁包里的etc目录还在,并且能清楚地对应到安装的补丁编号。没保留补丁解压目录的话,回滚无从谈起。所以安装前解压的补丁目录,回滚前要保留好,不要装完就删。
确认方式:
$ORACLE_HOME/OPatch/opatch lsinventory | grep -i "2xxxxxxx" $ORACLE_HOME/OPatch/opatch rollback -id 2xxxxxxx回滚前一样要停监听、停数据库,步骤和安装时保持一致。
6.2 回滚顺序与注意事项
回滚命令:
cd /u01/patch/2xxxxxxx $ORACLE_HOME/OPatch/opatch rollback -id 2xxxxxxx如果安装时执行过SQL脚本更新数据字典,回滚时通常也需要执行对应的回滚脚本。这个在补丁的README里会有说明,务必先去读README,不要想当然认为卸载了二进制就完事。
回滚完成后的验证和安装后类似,启动数据库,检查dba_objects中的无效对象,必要时重新执行utlrp.sql。另外提醒一点:回滚操作会让数据库与之前的安全基线不一致,如果是在安全测试前的窗口内回滚,需要评估是否有风险。
6.3 无法回滚时的应急方案
如果回滚命令报错,说找不到补丁ID,但有完整的Oracle Home备份,最稳妥的恢复方案就是直接用备份恢复Oracle Home。过程是:
- 停止所有Oracle相关进程和监听
- 把
$ORACLE_HOME改名备份,比如mv $ORACLE_HOME $ORACLE_HOME_failed - 解压之前打包的Oracle Home备份到原路径
- 启动数据库验证
这个方法虽然粗暴,但在补丁把Oracle Home目录搞乱、清单不一致时反而是最可靠的兜底方案。
7. 给后来人的几条实在建议
- 安装补丁前,把
$ORACLE_HOME/OPatch/opatch lsinventory的输出保存一份,方便安装后做对比。这条命令的输出包含了所有已安装补丁清单,是判断补丁应用是否成功最可靠的依据之一。 - 尽量在测试环境完整走一遍同样的流程,尤其是
catbundle.sql这一段,因为不同数据库里的对象数量、第三方包依赖情况差异很大,脚本执行的耗时和报错概率也完全不同。 - 补丁安装窗口最好选择业务低谷期,并预留出回滚的时间。如果原计划2小时的窗口,最后折腾了5个小时才搞定,这种情况并不少见。
- 记得检查
alert_<SID>.log,补丁应用过程中如果出现ORA-600系列错误,日志里会有详细记录,很多问题在测试阶段就能发现。
Oracle 11g已经是生命周期末期的产品,但现实中仍有大量单库环境在跑。给这种环境打PSU补丁,本质上就是给老系统补上一层层安全与稳定性的保护。只要按部就班把准备、预检、安装、验证、回滚预案这五件事做好,过程并不神秘,稳定性也完全可控。