
1. PSU补丁到底在修什么为什么说“单库环境也不能跳过”先聊一个很多人都会问的问题单库环境不搭建RAC也不做Data Guard就一个孤零零的数据库实例在跑是不是就可以不打补丁了我的答案很直接——恰恰相反单库环境才是最需要认真对待补丁的场景。RAC集群节点多出了问题还能挨个滚动处理单库环境一旦因为一个已知Bug挂掉那就是全站不可用连个托底的节点都没有。Oracle 11g这些年积累的PSUPatch 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 statusINVALID;如果有无效对象出现特别是与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/xxxlocalhost: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注册过系统服务或防火墙规则只在安装时开放过的场景。遇到这种用systemctlLinux 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 statusINVALID;针对具体的包如果是应用自己的包让应用侧重新编译发布如果是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补丁本质上就是给老系统补上一层层安全与稳定性的保护。只要按部就班把准备、预检、安装、验证、回滚预案这五件事做好过程并不神秘稳定性也完全可控。