简介:Oracle 19c TZ41补丁P35099667-190000-MSWIN-x86-64.zip是专为Windows 2008及以上版本打造的时区更新文件,面向数据库管理员、运维工程师以及需要精确处理跨时区数据的企业技术团队,用于解决Oracle数据库在夏令时规则切换、时区数据库版本滞后等场景下出现的时间偏差、性能下降与安全风险。压缩包内共10个文件,6个dat文件存储最新时区定义与夏令时规则,2个xml文件描述补丁配置与元数据,2个txt文件提供安装说明与校验信息,整体大小仅352KB,体积小巧、定位集中。目前已有703人参与学习,适合作为生产环境升级前的预发验证素材,或纳入日常数据库维护参考。应用补丁后,数据库能够识别更多全球区域的时间规则,有效减少因时区数据不准确引发的业务错误,同时提升系统在全球化部署下的稳定性与合规性,是保障Oracle 19c长期健康运行的关键维护项之一。
1. oracle19c 打 TZ41 补丁:p35099667-190000-MSWIN-x86-64.zip 到底解决了什么
把 Windows 上的 Oracle 19c 时区版本从旧版升到 TZ41,是很多 DBA 容易高估的一件事。拿到 p35099667-190000-MSWIN-x86-64.zip 这个文件,很多人以为解压、运行 opatch apply 就结束了,实际上补丁打完只完成了一半。真正决定时区能不能正确解析的,是数据库数据字典里的时区版本号,它不会跟着 opatch apply 自动变成 41。这个文件是 Oracle 19c 在 Windows x86-64 平台上的时区补丁包,解决的是夏令时规则变更、历史时区偏移修正以及客户端连接时区错乱的问题。适合那些在做 19c 安装、维护,或者已经被应用反馈"时间差了一小时"折磨过一轮的工程师。下面我按自己维护 Windows 单机和 RAC 环境的习惯,把从装前检查到打完收尾的完整路径写清楚。
2. 装前检查:补丁文件、OPatch 版本与 Windows 环境三件套
2.1 先看清 p35099667 是什么类型的补丁
看到 p35099667-190000-MSWIN-x86-64.zip 这种命名,第一件事不是解压,而是识别类型。p35099667 是 My Oracle Support 上的补丁号,190000 是补丁适用的基线发行版(19.0.0.0.0),MSWIN 代表 Microsoft Windows 平台,x86-64 就是 64 位。这类 TZ 补丁的典型特征是:zip 内包含一个新的时区数据文件,以及配套的数据库脚本目录。
常见做法是在 MOS 检索这个补丁号,先看补丁说明里的 "Product" 和 "Bugs Fixed"。TZ 补丁会明确写"升级时区文件到版本 41",并列出修正了哪些地区的时区规则。我一般会额外注意一个细节:补丁说明里有没有标注"需要 X 版本以上的 OPatch"。很多翻车事故就是从这里开始的,补丁本身没问题,OPatch 太旧跑不动前置检查。
另一个容易忽略的点:时区补丁和常规 PSU 补丁在目录结构上有区别。TZ 补丁的 zip 解压后,补丁目录里除了 patch files,还会有专门的 etc 目录和 datapatch 相关脚本。如果你解压后发现目录结构和普通补丁不一样,不用慌,这是时区类补丁的正常形态。
提示:先把 zip 里的补丁说明文档(通常叫 README.txt 或 html 文件)解压出来单独看,里面会写清楚"使用前必须满足的条件"和"应用后必须执行的后续步骤"。这一步别省,Windows 平台的时间戳坑和权限坑往往就藏在文档后半段。
2.2 用 opatch lsinventory 核对当前时区版本与 OPatch 版本
装前检查的核心是回答三个问题:当前 OPatch 版本够不够、当前时区版本是什么、Oracle Home 的 inventory 是否正常。Windows 上和 Linux 不一样,opatch 不是 opatch 脚本,而是%ORACLE_HOME%\OPatch\opatch.bat,需要以管理员身份打开 cmd 执行。
set ORACLE_HOME=C:\app\oracle\product\19.0.0\dbhome_1 "%ORACLE_HOME%\OPatch\opatch.bat" version "%ORACLE_HOME%\OPatch\opatch.bat" lsinventory第一行把 ORACLE_HOME 指向你要打补丁的 19c 家目录,第二行查 OPatch 自身版本,第三行列出已安装补丁清单。这里opatch.bat version的输出会直接显示类似 "OPatch Version: 12.2.0.1.28" 这样的数字,把这个值和补丁说明里的最低要求比一下。如果版本低于要求,先去下载对应 OPatch 补丁更新它,否则后面opatch apply大概率卡在 prerequisite check。
lsinventory的输出里,我们会关心两处:一处是Patch history列出的历史补丁,另一处是Oracle Home路径是否正确。Windows 上如果 Oracle Home 路径带空格,比如C:\Program Files\Oracle\...,命令里必须用引号包住,这是我踩过的实际坑。
接着进 SQL*Plus 查数据库当前的时区版本,这一步和 opatch 是两套体系,容易混为一谈:
SELECT version, version_time FROM v$timezone_file; SELECT DBMS_DST.get_latest_timezone_version FROM dual;第一条语句查的是当前数据库实际使用的时区文件版本号,19c 初始安装的版本可能在 32 到 36 之间,具体取决于你安装时用的补丁基线。第二条语句查的是数据库软件里自带的最新时区版本。如果第一条返回 40、第二条返回 41,说明数据库还在旧时区版本,正是这个补丁要解决的问题。
2.3 Windows 下解压 zip 的坑:路径过长、杀毒软件与目录权限
时区补丁虽然不大,但 Windows 解压有个经典玄学问题:路径超过 260 个字符时,系统自带解压工具会报"文件名太长"或直接静默漏文件。p35099667 补丁目录内部嵌套很深,文件名又长,我习惯先建一个短路径的临时目录,比如C:\temp\p35099667,用 7-Zip 或者 WinRAR 右键解压,不要直接双击打开的 zip 浏览器里复制文件。
mkdir C:\temp\p35099667 cd C:\temp\p35099667 "C:\Program Files\7-Zip\7z.exe" x "D:\download\p35099667-190000-MSWIN-x86-64.zip" -oC:\temp\p35099667 -y代码里的-o参数指定解压目标目录,注意-o和目标路径之间没有空格。解压完成后,进目录确认补丁子目录名字:通常是一个以补丁号35099667开头的目录,里面有etc、files和bin等子目录。如果目录名是乱的或者没有etc目录,说明解压不完整,重新解压或者换一台机器解压再拷贝过来都行。
杀毒软件是另一个容易让人误判的变量。Windows Defender 或者第三方杀软经常把 Oracle 的 opatch 脚本或时区数据文件当可疑文件处理,概率不大但一旦触发就是莫名其妙的"找不到文件"。我一般会先把解压目录加到杀软白名单,同时把%ORACLE_HOME%目录也临时排除扫描,打完补丁再恢复。这一步属于运维习惯,不属于官方文档要求,但值得做。
最后是权限检查:打开 cmd 时必须右键"以管理员身份运行",否则 opatch 没有权限写%ORACLE_HOME%\inventory。如果你用的 Windows 用户不是管理员,后续opatch apply会在 inventory 更新那一步直接失败,报错 ORA- 或者 "insufficient privileges",看了半天也不知道问题在哪。
3. 在 Windows 上用 opatch apply 打 TZ41 补丁的全过程
3.1 停止数据库与监听,准备干净的维护窗口
打时区补丁要在数据库完全关闭的状态下进行。Windows 上 Oracle 数据库通常注册成 Windows 服务,实例没停干净的话,时区数据文件会被进程占用,opatch 写不进去。
net stop OracleServiceORCL lsnrctl stop第一条net stop OracleServiceORCL按你的实际服务名改,比如OracleServiceORCL或OracleServiceORCL19C;第二条停止监听。如果这台机器上还跑着 OEM 代理或者其它 Oracle 相关服务,也一并停掉。RAC 环境则要一个节点一个节点来,两个节点不能同时打,通常是节点 1 停实例打完再启动,节点 2 再重复。
停止服务后用任务管理器确认没有oracle.exe进程残留。Windows 上经常出现服务停了,但后台还有一个进程卡着的情况,这种时候 opatch apply 就会在拷贝文件阶段报file is in use。干脆重启一次机器再执行 apply,反而最省时间。
3.2 执行 opatch apply 并读懂输出
进入补丁目录执行 apply,命令里两个关键点:一是必须以管理员 cmd 运行,二是要在补丁目录内执行,或者用-phBaseDir指定补丁目录。
cd /d C:\temp\p35099667\35099667 set ORACLE_HOME=C:\app\oracle\product\19.0.0\dbhome_1 "%ORACLE_HOME%\OPatch\opatch.bat" apply -phBaseDir C:\temp\p35099667\35099667-phBaseDir指向补丁解压后的子目录。如果目录里有多个补丁子目录,也可以先跑一次opatch.bat apply -report看预检结果,-report只做检查不实际应用,输出里会列出每个补丁是否满足前置条件。看到类似Patch [35099667] can be applied.这样的行,再真正执行 apply。
apply 过程会有大量输出,不需要逐条看,但几个关键行要盯住:Prerequisite check "CheckApplicable" for patch ... passed表示前置检查通过;Patch ... applied successfully或者OPatch succeeded.表示补丁写入成功。如果中间出现OUI-开头的错误码,说明卡在 OUI 相关检查,多半是 OPatch 版本或 inventory 权限问题,往下看第 5 章排查。
这里要特别说明:opatch apply做的是把新的时区数据文件放入%ORACLE_HOME%\oracore\zoneinfo并更新 inventory 记录。这一步完成后,软件的时区文件已经是 41,但数据库数据字典里记录的时区版本还没有变,所以别急着以为大功告成。
3.3 打补丁后真正常被漏掉的 datapatch 环节
19c 引入 datapatch 工具来处理数据库内部的补丁动作,时区补丁也不例外。数据库需要先启动到 open 状态,再执行 datapatch,否则会报实例未启动。
set ORACLE_HOME=C:\app\oracle\product\19.0.0\dbhome_1 set ORACLE_SID=ORCL net start OracleServiceORCL "%ORACLE_HOME%\OPatch\datapatch.bat" -verbosenet start把服务拉起后,执行 datapatch 时无需手动startup,服务起来后实例会自动 open。-verbose会输出每个补丁在数据库内部的执行进度,包括 SQL 脚本和应用状态。跑完后查一下select * from dba_registry_sqlpatch;,应该能看到补丁 35099667 的ACTION为APPLY、STATUS为SUCCESS。
但 datapatch 成功 ≠ 时区版本升级完成。datapatch 执行的是补丁自带的数据库端脚本,它不会替你调用 DBMS_DST 升级时区。这就是标题里 TZ41 补丁整个流程里最容易踩的坑:三件套做完,数据库时区版本还停在旧值,应用层查出来的时间照样是错的。下一章专门讲这一步怎么做。
4. 时区版本升级不止是打补丁:用 DBMS_DST 把 TZ 升到 41
4.1 为什么 opatch apply 之后还要单独做时区升级
需要先理解 Oracle 的时区体系:数据库里有两份时区数据,一份是软件文件层面的timezone.dat(存在%ORACLE_HOME%\oracore\zoneinfo),另一份是数据字典里注册的时区版本号。应用连接的TIMESTAMP WITH TIME ZONE类型数据,解析和显示依赖的是数据字典这一份,不是文件这一份。opatch apply 更新的是前者,DBMS_DST 负责的是后者。
所以打补丁后,即使v$timezone_file显示文件版本已经是 41,只要数据字典没升级,应用写入的带时区数据依旧按旧规则解释。默认情况下,Oracle 允许新版本向下兼容读取旧数据,但写出的新数据可能错一小时,这在夏令时切换的地区尤其明显。
另一个思考角度是回退。DBMS_DST 提供的升级是有完整"后悔药"机制的:在升级过程中任意时间点,只要没有执行END_UPGRADE,你都可以通过DOWNGRADE_DATABASE回到旧版本;一旦执行了结束标志,就回不去了。这也是为什么凡是涉及时区的维护,我都建议在业务低谷窗口做,并且严格执行下面这套步骤。
4.2 最小停机窗口的升级步骤:prepare 与 upgrade 分开跑
19c 的 DBMS_DST 流程分两段:先是生成并填充升级信息,再执行实际升级。我们这里用两张表来理解:dba_dst_affected_tables记录哪些表里有需要升级的带时区列;升级时 Oracle 会开启一个全局数据字典锁,把所有受影响的数据做一次内部转换。这两步分开跑,可以在 prepare 完成后先观察受影响行数,再决定是否继续。
SET SERVEROUTPUT ON BEGIN DBMS_DST.BEGIN_PREPARE( parallel => TRUE ); END; / BEGIN DBMS_DST.UPGRADE_DATABASE( upgrade_mode => DBMS_DST.UPGRADE, parallel => TRUE ); END; / BEGIN DBMS_DST.END_UPGRADE; END; /执行这段前,数据库必须处于startup upgrade状态。最简单的方式是:先把服务停了,然后在 cmd 里用sqlplus / as sysdba登入,执行startup upgrade;,再跑上面的 PL/SQL。三个匿名块对应的动作分别是:开始准备阶段(扫描所有带时区列的表并生成升级计划)、执行升级(真正把行内时区数据从旧版本转换成 41)、结束升级(写 registry 并释放锁)。
parallel参数这里值得展开。设成TRUE会在升级时使用多进程并行处理受影响表,速度明显快,但代价是内存和 undo 表空间消耗变大。如果你的数据库是 4 核以下的小机器,我建议把两个块里的parallel都改成FALSE,宁可慢一点也别在升级中途把共享池挤爆。如果是 RAC,不要同时跑两个节点的 DBMS_DST,文档不允许,实际操作也一样会冲突。
升级过程中如果想看进度,另开一个会话执行:
SELECT session_id, state, description FROM dba_dst_upgrade_sessions;这个视图能看到升级会话处于PREPARE、UPGRADE还是COMPLETED。升级过程中实例不能被重启,一旦中途断开,整个升级会话会处于不可用状态,需要从BEGIN_PREPARE重新来。这也是为什么一定要挑维护窗口做。
4.3 应用层 JDBC / ODBC 客户端的时区数据同步
数据库升到 TZ41 之后,应用连上来还会有一个隐性裂缝:客户端的 Java 运行时和数据库使用的 IANA 时区数据不是同一份。数据库已经按 41 规则解析,但 JDBC 驱动或操作系统的时区库还停留在旧版,两边算出来的偏移不一致,表现就是应用查到的时间在夏令时切换日前后总差一小时或者 30 分钟。
这块我一般会在升级文档里单独写一条操作项:给应用服务器上的 JDK 更新时区数据,JDK 自带的tzupdater工具可以做这件事,把客户端时区数据升到和 TZ41 匹配的版本。操作上,应用版本要有发布窗口,DBA 要和开发团队约定好,数据库升级后一周内必须同步客户端时区库,否则没法定位是数据库还是应用的问题。
注意:时区升级会短暂锁住
dba_dst_affected_tables里涉及的表,升级期间这些表上的 DML 会受影响。准备阶段完成后,用下面这条 SQL 看一下自己库里的受影响范围,决定要不要把维护窗口拉长:
SELECT COUNT(*), owner, table_name FROM dba_dst_affected_tables GROUP BY owner, table_name;如果查出来只有几十行,说明库里带时区列的数据很少,升级会非常快;如果是几百上千万行的日志表,那这个窗口至少要按小时算。
5. TZ41 补丁常见问题排查:从 OPatch 报错到时区升级失败的 5 个坑
5.1 现象:opatch apply 前置检查失败,报 OUI 错误
做opatch apply时输出里出现OUI-10054或类似的 Error,前置检查直接红叉。这个错误本身不提示 OPatch 版本,但如果去查opatch version会发现版本明显偏老,低于补丁说明里的要求。
原因:TZ 补丁通常在 OPatch 12.2.0.1.x 以上版本才支持完整的前置检测逻辑,老版本解析不了补丁包里的元数据。
解决:先更新 OPatch,再重新执行 apply。更新 OPatch 的方式是单独下载 OPatch 补丁包,解压后用其中的opatch.bat覆盖%ORACLE_HOME%\OPatch目录,覆盖前备份旧目录。更新完重新跑opatch.bat version确认,再进补丁目录执行 apply。
5.2 现象:zip 解压后补丁目录里缺少关键子目录
解压完p35099667-190000-MSWIN-x86-64.zip,发现目录里只有零散的 xml 文件,或者没有files、bin目录,直接 opatch apply 时报"补丁不完整"。
原因:Windows 默认解压工具碰到超长路径会静默跳文件,而且跳过的文件不提示,导致补丁目录看起来完整实际缺东西。
解决:用 7-Zip 重新解压到短路径(如C:\temp\p35099667),解压后对比目录长度,确认补丁目录名完整没截断。换工具后目录结构正常,apply 不再报缺文件。这个坑在 Windows Server 上比想象中常见,尤其是装在C:\Program Files下的 Oracle Home,路径叠加解压临时目录很容易超过 260 字符。
5.3 现象:datapatch 跑完,v$timezone_file 仍是旧版本
打完补丁、datapatch 显示 SUCCESS,但执行SELECT version FROM v$timezone_file;看到的还是 40 甚至更低。
原因:datapatch 只负责数据库补丁注册,时区版本的数据字典升级必须通过 DBMS_DST 显式执行。这是整个流程里最容易误判的一步,操作上补丁文档里写了,但很多 DBA 看完 opatch 成功就收工了。
解决:执行第 4 章的 PL/SQL 升级块,确认registry$dst中timezone_version变成 41 才算完。任何时候收到应用反馈"时间差一小时",先把这条 SQL 的结果拉出来,别一上来就查应用代码。
5.4 现象:DBMS_DST 升级中途报 ORA-04030 或 ORA-01650
跑DBMS_DST.UPGRADE_DATABASE时数据库在升级阶段弹 ORA-04030(进程内存不足)或 ORA-01650(undo 空间不足),升级会话中断。
原因:parallel => TRUE时并行进程吃掉大量共享池内存,同时数据转换产生大量 undo,小机器上资源直接被推翻。不是补丁问题,是资源估算没做足。
解决:改串行执行,两个块都设置parallel => FALSE;同时把共享池调大,比如ALTER SYSTEM SET SHARED_POOL_SIZE=512M;(视内存实际大小调整,不要照抄)。因为升级会话中断后要重新走 BEGIN_PREPARE,所以建议先调完参数再开始,别中途救火。
5.5 现象:升级完成后应用查询带时区字段依然偏移
数据库侧v$timezone_file和registry$dst都已经是 41,但应用跑出来的时间差一小时,白天还是深夜,时间规则混乱。
原因:客户端时区数据没有同步。数据库按 TZ41 规则解析夏令时,但应用服务器的 JDK 或操作系统的时区文件还是旧规则,两边对"这一秒该按哪个偏移量算"结论不一致。
原因确认方法:在应用服务器上用 Java 直接TimeZone.getTimeZone("America/New_York")看偏移,再在数据库执行SELECT TZ_OFFSET('America/New_York') FROM dual;,对不上就是客户端时区数据落后。解决是给 JDK 更新时区数据、同步操作系统时区库,然后重启应用。这步经常要跨团队协调,但本质就是两边时区规则版本不一致造成的"假时区 bug"。
6. 打完 TZ41 怎么验证:文件层、数据字典层、业务层都不放过
6.1 三步验证法
打完整个流程,我习惯按三层来验证,缺一层都不放心。第一层是软件文件层,确认%ORACLE_HOME%\oracore\zoneinfo里的 timezone.dat 时间戳已经变成补丁应用日期;第二层是数据字典层,确认v$timezone_file和registry$dst的timezone_version都是 41;第三层是业务层,随便对一张带TIMESTAMP WITH TIME ZONE的表做读写测试,把北京时间和一个有夏令时的时区放一起对比。
SELECT version, version_time FROM v$timezone_file; SELECT * FROM registry$dst; ALTER SESSION SET TIME_ZONE = 'America/New_York'; SELECT TO_CHAR(SYSTIMESTAMP AT TIME ZONE 'America/New_York', 'YYYY-MM-DD HH24:MI:SS TZR') AS ny_time, TO_CHAR(SYSTIMESTAMP AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD HH24:MI:SS TZR') AS bj_time FROM dual;如果两条结果的时差符合当期夏令时规则,并且冬季和夏季切换日前后各测一次都对得上,业务层的验证才算通过。我一般会把三条 SQL 的输出存一份到运维记录里,作为下次排障的时间基线。
6.2 回退的唯一后悔药:DOWNGRADE_DATABASE
升级后如果应用侧出现大面积兼容问题,并且确认是时区版本导致,唯一后悔药是DBMS_DST.DOWNGRADE_DATABASE。这个操作同样要在实例startup downgrade状态下执行,并且必须在升级会话结束后使用。降级前提前确认registry$dst里记录了旧版本号,Oracle 支持回退到升级前的那一版,但不能跳版本。
这段操作我实际只用过一次,是在测试库上验证流程时跑的。印象最深的是:降级执行的时间几乎是升级的两倍,因为它要把所有被升级过的行反转一遍。所以生产环境动手前,先问一句:业务到底能不能接受这个维护时长。打完补丁不升级时区,等于白忙;升级了发现应用没准备好,要么憋着长维护窗口降级,要么硬扛到客户端升级,两个都难受。希望这次的步骤和踩坑记录能帮你在 TZ41 这条路上少走一趟弯路。
本文还有配套的精品资源,点击获取