简介:Oracle 19c TZ41时区补丁更新包,面向Windows Server 2008及以上环境中的数据库管理员与运维工程师,用于解决时区数据文件过旧引发的夏令时规则失效、跨时区时间计算偏差以及相关安全漏洞问题。压缩包体积仅352KB,内含10个文件,主体为6个dat时区数据库数据文件,另含2个xml区域配置说明与2个txt安装及校验文档,结构简洁便于核对。目前已有707人学习/下载,是Oracle官方补丁P35099667在Windows x86-64平台的离线分发版本。通过部署该补丁,DBA可一次性完成Oracle 19c的TZ41时区数据更新,让全球业务数据的时间戳保持一致,规避因时区错误导致的数据偏差与合规风险。资源包内容可直接配合OPatch工具使用,适合需要为生产库补充时区补丁的中高级运维人员按官方流程离线安装。
1. 一份 p35099667-190000-MSWIN-x86-64.zip 到底在给 19c 换什么
新业务上线当天,Windows 服务器上的 Oracle 19c 应用会话开始抛ORA-01882: timezone region not found。SQL 没问题、网络没问题,日志里只有这一条,最后定位到数据库自带的时区文件太老,已经认不出客户端传过来的时区名。p35099667-190000-MSWIN-x86-64.zip 正是在这种场景下出现的:它是 Oracle 19c 在 Windows x86-64 平台上的 TZ41 时区补丁,核心工作是把数据库的 timezone 文件从旧版本升级到版本 41。文件名拆开看,35099667 是补丁号,190000 对应 19.0.0.0.0 基础版本,MSWIN-x86-64 指明平台。适合手里有 19c 实例、能拿到维护窗口的 DBA 和实施工程师阅读。下面按怎么打、怎么验、怎么回退的顺序展开。
2. 打 TZ41 之前,先确认你的数据库时区版本停在几
2.1 时区文件版本和补丁版本为什么会对不上
Oracle 数据库从安装包落地那一刻起,就会带一份时区数据文件,放在 ORACLE_HOME 的 oracore\zoneinfo 目录下。这份文件记录了全球各时区区域、夏令时规则和历史变更,几乎所有和TIMESTAMP WITH TIME ZONE、FROM_TZ、AT TIME ZONE相关的计算都依赖它。数据库内部用一位版本号来标识当前用的是哪一代数据,比如版本 32、33、34。外部世界每年都在调整夏令时规则,Oracle 于是按节奏发布 DST(Daylight Saving Time)补丁,把时区数据整体升级到新版本,TZ41 就是其中一个版本号。
很多人会误把 TZ41 当成一次例行补丁更新,实际上它不属于常规的 RU(Release Update),不修 SQL 执行计划、不碰内核代码,它只替换时区数据和相关的字典元数据。正因如此,它的安装方式和普通数据库补丁不太一样:普通 RU 可以在实例停止后直接 opatch apply,然后重启;而 TZ 补丁要求数据库完全关闭、软件安装完成,再以 UPGRADE 模式启动一次,执行专门的时区升级脚本,把数据库字典里记录的时区版本号从旧值刷到 41。补丁包里的二进制文件和数据库内部状态是两回事,这一步少做,打补丁等于打了半套。
那么一个问题自然来了:怎么知道当前实例版本是多少?常见做法是直接在 SQL*Plus 里查几个系统视图。注意查询结果里有三个数字容易混淆,下面单独列清楚。
2.2 三条 SQL 查清当前版本、活动版本与目标版本
在准备打 TZ41 之前,我通常会在一个维护窗口里先跑下面这几条查询,把“数据库当前生效版本”“软件支持的最新版本”“安装信息里登记的版本”全部记录下来,避免打到一半才发现版本已经是最新或比 41 还新。
-- 1. 当前数据库实际生效的时区文件版本 SELECT FILENAME, VERSION FROM V$TIMEZONE_FILE; -- 2. 当前软件安装包中能支持的最新时区版本 SELECT DBMS_DST.GET_LATEST_TIMEZONE_VERSION AS LATEST_TZ_VERSION FROM DUAL; -- 3. 数据库登记表里的时区版本,打补丁后需要跟踪这个数字 SELECT TIMEZONE_VERSION FROM REGISTRY$DATABASE;第一条从 V$TIMEZONE_FILE 读出实例正在使用哪一个时区文件,VERSION 列直接返回版本号。第二条调用 DBMS_DST 包的 GET_LATEST_TIMEZONE_VERSION 函数,返回的是当前 ORACLE_HOME 里安装的时区文件支持的最新版本号;在打完 TZ41 补丁后,这条查询应该返回 41。第三条查 REGISTRY$DATABASE,它记录了已安装组件和版本信息,时区升级脚本成功后,TIMEZONE_VERSION 会变成 41。
这三个数字的正常关系是:V$TIMEZONE_FILE 的 VERSION 小于或等于 LATEST_TZ_VERSION,REGISTRY$DATABASE 里的版本一般和 V$TIMEZONE_FILE 一致。如果出现 V$TIMEZONE_FILE 显示 32 而 LATEST_TZ_VERSION 已经返回 41,说明软件文件已经到位但数据库尚未执行时区升级脚本,或者补丁只做了一半,这正是最容易栽坑的状态。执行这三条语句不需要额外授权,但建议用 SYS 或具备系统视图查询权限的 DBA 账号跑,避免权限不足查不出结果。
2.3 什么时候必须打这个补丁:ORA-01882 与业务侧诉求
不是每个环境都需要立刻打 TZ41。最典型的需求信号是应用日志或 alert 日志里出现 ORA-01882,报错文本会带上一个时区区域名,比如客户端传来的 region 在当前时区文件里不存在。数据库时区文件是每年更新的,如果某地调整了夏令时规则,或者新版操作系统、新版 JDK 使用了新的时区 ID,旧数据库就可能不识别。
第二种诉求来自业务升级。某个业务系统要上线跨区域订单功能,产品方给出的环境要求里明确写着“数据库时区文件需要支持 TZ41”。这种时候即使线上没报错,也要提前准备。原因是时区数据影响的不只是新写入的数据,历史 TIMESTAMP WITH TIME ZONE 数据的解读也会依赖当前时区文件;等业务上线后再补,转换成本更高。
第三种情况是数据库本身在做大版本维护,比如准备把 19c 迁移到新的服务器,或要从备库搭一套测试环境。新版本数据库默认带新时区文件,如果源库是旧的,同步过程中会提示时区版本不一致。常见做法是先在源库把时区升到同一版本再迁移,避免过程中 ORA-01882 集中爆发。这里有一个前提要提前确认:补丁包对应的 190000 基础版本必须不大于你当前的数据库版本。如果数据库已经打了较高的 RU,一般也兼容,但务必先看补丁解压后自带的 README 中关于适用版本和 OPatch 最低版本的要求,这个信息在下一章准备工作中就会用到。
3. Windows 平台的补丁落地准备:OPatch 环境、解压与预检
3.1 把 zip 解开成 OPatch 能认的补丁目录
Windows 上打补丁之前,我坚持先把 zip 包完整解压到一个独立目录,再运行 OPatch,而不是直接对 zip 操作。原因有两个:一是 OPatch 需要读取补丁目录里的 etc、files、patch 等结构,压缩包里只有打包后的原始内容;二是直接对 zip 执行时,一旦路径带中文、空格或盘符权限不足,OPatch 的日志会变得极难排查,而且容易误判成补丁本身的问题。
cd /d D:\oracle_install tar -xf p35099667-190000-MSWIN-x86-64.zip cd 35099667 dir如果系统里没有 tar,用常见的图形化解压软件也可以,结果一致。解压完成的目录名一般就是补丁号 35099667,以实际解压结果为准。目录内部会有一个 README 文本,建议先打开看一遍,里面一般写明适用版本、OPatch 最低版本要求、是否需要先停监听。这一步花不了几分钟,但能避开后面一半的坑。
这里有个非常现实的细节:Windows 的用户目录、下载目录往往位于 C 盘且权限敏感,OPatch 写入日志和临时文件时容易失败。我一般把补丁放在 D 盘根目录下新建的补丁目录,全程用管理员身份打开 CMD。解压之后不要急着 apply,先做环境变量设置和 OPatch 自检,否则 Windows 上最容易遇到“明明文件解压了,opatch 却说找不到补丁”“明明以管理员打开了,却无法访问 inventory”这类问题。
3.2 检查 OPatch 版本、库存与冲突:一条命令做完整预检
Windows 平台上的 OPatch 是位于%ORACLE_HOME%\OPatch目录下的一组批处理程序,入口是 opatch.bat。打 TZ41 前,我会先确认当前 OPatch 版本是否符合补丁 README 的最低要求,并检查现有软件库存是否完整。下面这段命令先固化环境变量,再做版本自查和库存检查。
set ORACLE_HOME=C:\app\oracle\product\19.0.0\dbhome_1 set PATH=%ORACLE_HOME%\bin;%ORACLE_HOME%\OPatch;%SystemRoot%\system32;%SystemRoot% cd /d %ORACLE_HOME%\OPatch opatch.bat version opatch.bat lsinventoryORACLE_HOME 必须替换成你实际环境的路径,Windows 上可以通过系统服务属性或注册表里的 ORACLE_HOME 值确认。PATH 里把 ORACLE_HOME\bin 放在最前面,是为了避免同一台机器上装了多套 Oracle 软件时,sqlplus、lsnrctl 等命令指向了别的 HOME;OPatch 目录放在第二位,保证 opatch 命令来自当前 HOME。SystemRoot 相关项保留是为了让 Windows 自带命令不丢失。opatch.bat version 返回的版本值,可以和补丁 README 中的要求对比;opatch.bat lsinventory 列出当前 HOME 已安装的补丁和基础版本,如果列出的 19.0.0.0.0 基础版本信息完整,说明 inventory 可读。
预检命令通常是这一条,它只检查冲突,不做任何修改:
cd /d D:\oracle_install\35099667 %ORACLE_HOME%\OPatch\opatch.bat prereq CheckConflictAgainstOHWithDetail -phBaseDir .这条命令会读取当前补丁目录,检查补丁与 HOME 里已装组件是否有文件冲突或依赖冲突。如果返回成功,再执行正式 apply 会省很多事。常见报错包括 OPatch 版本过低、补丁已应用、与已装补丁冲突,出现这些就不用往下走了,先把前置条件解决。
3.3 数据库侧准备:备份、服务清单与停机窗口
OPatch 环境就绪后,还要做三件数据库侧的事:全量备份、记录停机前状态、整理实例和监听的服务名。TZ 补丁虽然不碰业务数据,但时区升级脚本会对带时间戳时区的列做内部转换,一旦中途失败,恢复现场只能靠备份。我的血泪经验是:这一步千万别省,省了就是拿生产数据赌补丁没问题。
set ORACLE_HOME=C:\app\oracle\product\19.0.0\dbhome_1 set PATH=%ORACLE_HOME%\bin;%PATH% rman target / nocatalog backup database format 'D:\oradb_backup\before_tz41_%U.bck' include current controlfile plus archivelog delete input;RMAN 全备会花一定时间,但它是回退的唯一后悔药。Windows 上如果开了归档模式,include current controlfile plus archivelog 会把当前控制文件和归档一起备份,恢复到补丁前状态足够。如果环境里没有配置归档或数据量特别大,也可以退而求其次,至少备份 ORACLE_HOME 下 oracore\zoneinfo 整个目录和参数文件,再把数据文件在实例关闭后用操作系统复制工具冷备一份。冷备在 Windows 上做文件级复制比较简单,但必须保证数据库完全关闭后再复制,不能开着库复制数据文件。
备份完成后,记录当前时区版本、DBTIMEZONE、当前活动会话数,再确认 Windows 服务名。19c 单机在 Windows 上的数据库服务一般叫 OracleService ,监听服务叫 OracleOraDB19Home1TNSListener。准备停库的顺序是:先停应用连接,再停监听,最后停数据库服务。
lsnrctl stop net stop OracleServiceORCLnet stop 后面的服务名要替换成实际 SID。如果机器上有多个实例,所有使用同一 ORACLE_HOME 的实例都要在后续 apply 前处于停止状态。停完后用 sqlplus 以 sysdba 连接一次,系统提示连接到空闲例程或报 ORA-01034,说明实例确实处于关闭状态。这一步虽然繁琐,但能避免 opatch apply 时因为文件被占用而失败。
4. 正式应用 TZ41 补丁:关库、apply、启动到 UPGRADE 模式并升级时区
4.1 数据库处于完全关闭状态时才能执行 opatch apply
补丁安装阶段必须保证数据库实例完全关闭,因为 OPatch 要替换 ORACLE_HOME\oracore\zoneinfo 目录下的时区数据文件,Windows 对正在被进程占用的文件是直接锁定的。如果有 sqlplus、监听或后台 agent 还连着实例,文件替换就会失败,而且 OPatch 会给出让人摸不着头脑的“无法访问文件”报错。
确认实例关闭后,进入补丁目录执行 apply:
cd /d D:\oracle_install\35099667 %ORACLE_HOME%\OPatch\opatch.bat apply这个命令没有额外参数,OPatch 会根据当前 HOME 的 inventory 判断补丁是否适用、是否存在冲突。执行过程中会显示补丁 ID、目标 HOME、平台信息,最后出现 OPatch succeeded 才代表软件文件替换完成。如果中途失败,先不要重启实例,直接看日志。日志位置一般在%ORACLE_HOME%\cfgtoollogs\opatch\下,文件名带时间戳,搜索 ERROR 或 FAILED 关键字定位原因。常见失败原因之一是上一次补丁会话没退干净,另一个是 PATH 里混入了其他 HOME 的 OPatch,这两个问题我在第 3 章已经交代过,这里不再重复。
还有一个细节必须注意:apply 成功后,当前 ORACLE_HOME 下会多出一份新的时区文件,但数据库字典里登记的版本仍然是旧值。换句话说,opatch apply 只完成了“软件替换”,“数据库内部升级”还没做。很多第一次打 TZ 补丁的人在这里误以为收工了,直接 open 数据库,结果业务还是报 ORA-01882,因为数据库仍在使用旧的内部时区信息。
4.2 运行 utltz_upg.sql 把数据库内部时区版本刷到 41
软件文件替换完成后,要把数据库启动到 UPGRADE 模式,再执行官方的时区升级脚本。UPGRADE 模式会跳过很多常规检查,允许脚本修改数据字典。以管理员身份打开 CMD,进入 SQL*Plus:
sqlplus / as sysdba startup upgrade如果前面已经用 net stop 停了服务,startup 命令会把实例拉起。执行成功后,数据库处于受限的升级状态,此时其他会话无法正常访问业务数据。接着执行时区升级脚本:
@?/oracore/zoneinfo/utltz_upg.sql这里 ? 是 SQL*Plus 对 ORACLE_HOME 的简写,等价于写完整路径。脚本内部会调用 DBMS_DST 包,完成 begin-upgrade、数据转换、end-upgrade 三个核心动作,并更新 REGISTRY$DATABASE 中的时区版本登记。
脚本执行时间取决于数据库中 TIMESTAMP WITH TIME ZONE 数据的数量和表数量,几十分钟到几个小时都可能。执行期间不要另开窗口做 DDL,也不要强行中断 SQL*Plus;如果网络中断导致会话断开,脚本进程一般也会被回滚或停止,后续需要重新启动到 UPGRADE 模式查看状态,不要贸然重复执行。脚本输出中如果出现 Timezone upgrade completed 或类似字样,说明内部时区升级已成功。之后可以顺手查询一次:
SELECT VERSION FROM V$TIMEZONE_FILE;如果返回 41,软件和数据库两部分都完成了,下面进入正常重启。
提示:utltz_upg.sql 执行期间只保留当前这一个 SQL*Plus 会话,其他窗口不要发起 DDL 或导入导出操作。中断后先确认升级状态,再决定是否重跑。
4.3 重启到正常模式:确认补丁生效的完整命令序列
时区脚本执行完毕后,数据库必须重新启动到正常模式。UPGRADE 模式下的数据库不能服务业务,而且很多内部包的状态是升级模式专用,直接接着跑业务会有风险。完整命令序列如下:
shutdown immediate startup重启到正常模式后,第一时间执行三条确认语句:
SELECT VERSION FROM V$TIMEZONE_FILE; SELECT TIMEZONE_VERSION FROM REGISTRY$DATABASE; SELECT DBMS_DST.GET_LATEST_TIMEZONE_VERSION FROM DUAL;三处的期望结果都应该是 41。第一个确认当前实例使用的时区文件版本,第二个确认数据字典里的登记版本,第三个确认软件支持的最新版本。三条都变成 41,TZ41 补丁才算真正生效。如果第二条还显示旧版本,说明 utltz_upg.sql 没有成功提交,需要回到 UPGRADE 模式重跑;如果第三条还是旧版本,说明 opatch apply 阶段就失败了,需要回头查 OPatch 日志。
实例正常起来后,再启动监听并做一次简单的连接测试:
lsnrctl start sqlplus system@ORCL能正常登录、执行任意查询,补丁安装阶段就结束了。逻辑上注意:Windows 服务负责拉起实例,实例正常启动后监听才能真正接受连接,所以顺序不能反过来。如果监听先启动,再打开实例,连接请求会在实例刚开始的几秒内失败,容易被误判成补丁故障。
5. TZ41 补丁在 Windows 上最常翻车的 5 个场景与排查记录
5.1 opatch apply 卡死或报 OUI 锁库:检查残留进程与服务
现象:opatch apply 启动后长期停在某个进度,或直接报 system inventory is locked。排查后发现任务管理器里有一个旧的 java.exe 一直在运行。
原因:之前某次 opatch 会话没有正常退出,命令行窗口关闭时把 java 进程留在了后台;也可能是另一个管理员窗口正开着 opatch,两个会话同时抢 inventory 锁。Windows 对文件锁的处理比 Linux 更敏感,进程还在,文件就释放不了。
解决:先结束所有与 OPatch 相关的 java 进程,再确认所有 Oracle 服务处于停止状态。打开任务管理器,按名称排序,结束 java.exe 和运行 opatch 的进程;然后重新打开一个干净的管理员 CMD,重新设置 PATH,再次执行 opatch apply。如果还锁,就重启一次 Windows,重启后第一时间只运行这一个补丁命令,成功率最高。
5.2 OPatch 报 missing prerequisite:先看两处版本
现象:apply 开始前的预检阶段就报 missing prerequisite 或 CheckMinimumOpatchVersion failed,补丁没有真正进入文件替换阶段。
原因:补丁 README 要求的 OPatch 最低版本高于当前 HOME 自带版本;或者当前数据库基础版本太老,已装的 RU 与补丁要求不对应。TZ 补丁虽然是时区数据,但它的应用机制对 OPatch 版本有要求,版本不够就会直接拒绝。
解决:先执行 opatch.bat version 拿到当前 OPatch 版本,再从补丁目录里打开 README 找到最低版本要求。如果 OPatch 太低,需要先单独升级 OPatch:到官方支持站点下载对应 19c 的 OPatch 更新包,解压后把整个 OPatch 目录覆盖到 ORACLE_HOME,覆盖前备份原目录。升级完 OPatch 后,再次执行 precheck,确认基础版本信息正常再 apply。这个过程不涉及数据库重启,只动软件目录,相对安全。
5.3 utltz_upg.sql 执行中报 ORA-01882:旧数据里的非法时区区域
现象:脚本执行到一半,冒出 ORA-01882: timezone region not found,紧跟着是一串疑似某张业务表的名称。
原因:数据库里已存在 TIMESTAMP WITH TIME ZONE 数据,其中某些行记录的是旧时区文件认识的区域名,而新时区文件统一了命名,或某些历史区域被移除、改名。脚本在数据转换阶段逐行读取这些列时,无法在新版本中找到对应区域,于是报错中止。
解决:先把当前升级状态清理掉,不能硬着头皮重跑。常见做法是重新启动到 UPGRADE 模式,查询当前升级状态;如果 begin-upgrade 已执行但未提交,需要先结束该状态。之后再从应用侧或数据清洗 SQL 找出报错行对应的时区列,把值改为新版本里存在的等价区域名。这个操作在数据量大的表上要特别小心,务必先备份该表再执行更新。这类问题在存量 TSTZ 数据较多的业务库中最容易出现,纯新建库反而很少遇到,排查时先分清是系统表还是业务表。
5.4 打完补丁后客户端依然报 ORA-01882:时区文件不只是数据库有
现象:数据库内三条查询都已显示 41,但应用在连接池里仍然抛 ORA-01882,错误堆栈里能看到 JDBC 或 .NET 驱动的类名。
原因:Oracle 客户端和数据库各自维护一套时区数据。JDBC 驱动用自带时区定义,.NET 驱动用对应 Oracle 组件的时区定义;数据库升到 TZ41 只解决了服务端,客户端驱动如果还停留在旧版本,它解析不了服务端返回的新区域,甚至应用传入的区域在客户端本地就查找失败。
解决:把应用服务器上的 JDBC 或 .NET 数据访问组件升级到与 19c 匹配的较新版本,并确认驱动附带的时区数据版本支持 TZ41。升级后清空连接池缓存,重启应用,再复现一次原报错 SQL。这类问题不在数据库补丁流程内,但它是“打了 TZ41 业务仍报错”的最高频原因,验证时一定要把客户端纳入检查范围。
5.5 二次 apply 或 rollback 异常:inventory 状态比文件更值得关注
现象:打了补丁后又想回滚,执行 opatch rollback -id 35099667,结果报 patch not found in inventory;或者想再 apply 一次,报 Patch is already applied。
原因:TZ41 补丁的安装状态记录在 ORACLE_HOME 的 inventory 里,不是看 zoneinfo 目录下有没有新文件。如果中途手动删除过补丁目录,或者之前用别的命令改过 inventory 记录,就会造成文件在但记录缺失,或记录在但文件不存在。
解决:回滚前先执行 opatch lsinventory -detail 确认补丁 ID 是否仍然记录在案;如果记录丢失,不要手动去把新文件换回旧文件,那样会把状态搞得更乱。最稳妥的路径是恢复补丁前做好的全量备份。所以我在第 3 章反复强调备份,TZ 补丁不像普通 RU 那样能靠 opatch rollback 干净复原,备份才是真正意义上的后悔药。
6. 打完 TZ41,怎么才算真正收工:三个验证信号与一张后悔药
6.1 三个验证信号
补丁结束不等于收工,我通常会按下面三个信号逐项验收:
| 验证信号 | 命令 | 期望结果 |
|---|---|---|
| 实例使用的新时区文件版本 | SELECT VERSION FROM V$TIMEZONE_FILE; | 41 |
| 数据字典登记的时区版本 | SELECT TIMEZONE_VERSION FROM REGISTRY$DATABASE; | 41 |
| 业务侧原始报错 SQL | 重新执行应用之前失败的 SQL 或典型时区函数 | 不再出现 ORA-01882 |
前两个信号说明数据库内部和外部的时区文件都就位,第三个信号才是业务真正关心的结果。如果应用之前只是报错而没有固定复现 SQL,可以让测试环境将新的时区区域写入一张带时间时区的临时表,再查回来,能够正常往返就说明链路通了。注意,第三个信号要覆盖数据库重新启动后、连接池刷新后的情况,避免测到的是旧缓存连接。
6.2 回退的边界:什么能退,什么不能硬退
TZ41 这个方向基本没有回头路。opatch apply 成功但数据库还未跑 utltz_upg.sql 时,回退还容易:直接执行 opatch rollback -id 35099667,数据库还是旧状态。一旦 utltz_upg.sql 在 UPGRADE 模式中执行成功,数据库可能已经对存量 TSTZ 数据做了转换,这时强行降级时区文件没有官方通道,硬退只会让数据解读出现偏差。我自己的习惯是:打这类补丁前先拿一个克隆环境完整跑一遍,把 utltz_upg.sql 的耗时、脚本输出、前后版本差异全部记录下来,生产环境照着同一套流程走。第一次打 TZ41 时我吃过一次亏:在 Windows 上没把 PATH 清理干净,opatch 用了另一个 HOME 的库存,apply 一路报错,白白消耗了停机窗口。从那以后,每次补丁前都把 PATH、ORACLE_HOME、服务状态三条写进检查清单,先验证后动手。
时区文件升级这种活,做对了没人夸,做砸了影响的是所有时间字段,风险极高。把软件替换和数据库升级两个阶段分开理解,把客户端驱动纳入验证范围,基本就能避开大多数坑。希望这次的步骤和踩坑记录能帮到你下一次打 TZ41 时少走弯路,一次过窗。
本文还有配套的精品资源,点击获取