简介:本资源是 Oracle Database 19c(19.0.0.0.190416DBRU)专用的时区版本35补丁包,专为解决 ORA-39405 错误及 TSTZ(Time Zone)版本不匹配问题设计,适用于已部署 Oracle 19.3 版本的 Linux-x86-64 环境,用于将数据库时区文件升级至 TSTZ v35。补丁经官方 Bug#31335037 修复验证,安装后可通过 SELECT * FROM v$timezone_file; 命令确认生效。压缩包共10个文件,含6个核心 dat 数据文件(承载时区定义)、2个 xml 配置文件(描述时区映射关系)及2个 txt 说明文档(含安装指引与注意事项),结构精简、部署路径明确,总大小仅394KB,便于快速集成与验证。目前已有2567人学习下载,适合 DBA 及数据库运维人员在生产环境升级、灾备同步或跨版本迁移中精准应对时区兼容性问题,提供即用型修复能力与可复现的版本验证方法。
1. Oracle 19c 时区补丁 P31335037:装错版本或漏打补丁,数据库时间就“穿越”了
某高校实验室部署一套跨时区日志分析系统,Oracle 19c 数据库跑在 Linux x86-64 环境下。上线第三天,运维同学发现凌晨 2:15 的交易记录被写成 1:15——不是程序逻辑错,也不是应用层时区设错,而是SYSTIMESTAMP和CURRENT_TIMESTAMP返回值在夏令时切换窗口期集体偏移一小时。查到最后,是数据库底层时区文件(timezone.dat)仍停留在 2019 年的旧规则,而操作系统已更新至 IANA 2021a 时区数据。Oracle 官方明确要求:19.0.0 基础版必须打上时区版本 35 补丁(p31335037)才能支持 2021 年起全球主要地区夏令时变更规则。这个补丁不依赖 OPatch 升级、不触发数据库重启,但一旦跳过,所有基于TIMESTAMP WITH TIME ZONE类型的审计、ETL、报表时间戳都会“玄学漂移”。它不是可选优化项,而是 19.0.0 在生产环境存活的底线配置。本文专为已装好 Oracle 19.0.0 数据库、但尚未同步时区规则的 DBA 和部署工程师而写——不讲原理空话,只列清:该下哪个包、解压到哪、OPatch 怎么调、打完怎么验、打崩了怎么救。
2. 下载与校验:认准 p31335037_190000_Linux-x86-64.zip,别被同名补丁带偏
Oracle 补丁命名体系里,“p31335037” 是补丁编号(Patch Number),“190000” 表示适用版本(19c Release 1,即 19.0.0),“Linux-x86-64” 是平台标识。注意:同编号补丁有多个子版本(如 p31335037_191000_Linux-x86-64.zip 对应 19.10),绝不能混用。19.0.0 数据库只能用 190000 后缀包,否则 OPatch 会直接拒绝加载。
2.1 从 Oracle Support 下载补丁包(需 MOS 账号)
访问 https://support.oracle.com → 登录 → 搜索补丁号31335037→ 进入补丁详情页 → 找到"p31335037_190000_Linux-x86-64.zip"→ 下载。
提示:页面可能同时列出
p31335037_190000_Generic.zip(通用版,含所有平台)和p31335037_190000_Linux-x86-64.zip(Linux 专用精简版)。优先选后者——体积小(约 12MB)、解压快、无冗余文件,且 Oracle 官方文档明确推荐 Linux 环境使用此包。
2.2 校验 ZIP 包完整性(防下载中断/传输损坏)
下载完成后,务必校验 SHA256 值。Oracle Support 页面补丁详情区底部会提供官方校验值(通常在 “Patch Information” 或 “Files” 标签页内)。以当前最新发布版本为例(2024 年 3 月更新):
$ sha256sum p31335037_190000_Linux-x86-64.zip a7e9b8c2d1f0e3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8 p31335037_190000_Linux-x86-64.zip若输出值不匹配,说明文件损坏,需重新下载。这是后续所有操作的前提——补丁包损坏会导致 OPatch 解析失败,报错OPatch failed with error code 73,且无明确提示原因。
2.3 解压到临时目录,不直解到 $ORACLE_HOME
将 ZIP 包解压到独立临时目录(如/tmp/patch_p31335037),而非直接解压到$ORACLE_HOME下:
$ mkdir -p /tmp/patch_p31335037 $ unzip p31335037_190000_Linux-x86-64.zip -d /tmp/patch_p31335037 $ ls -l /tmp/patch_p31335037/ total 8 drwxr-x--- 3 oracle oinstall 4096 Mar 15 10:22 31335037/ -rw-r----- 1 oracle oinstall 1234 Apr 2 09:15 README.txt解压后核心目录是31335037/(数字命名),内含etc/(元数据)、files/(实际替换文件)、custom/(自定义脚本)等子目录。关键点:files/oracore/zoneinfo/下的timezone.dat文件就是本次要升级的目标——它将从旧版(TZ version 32)覆盖为新版(TZ version 35)。不直解到$ORACLE_HOME是为避免权限混乱和误删风险;临时目录也方便打补丁失败后一键清理。
2.4 验证补丁元数据是否匹配当前数据库版本
进入解压后的补丁目录,检查etc/config/actions.xml中声明的兼容版本:
$ grep -A 5 "<oraclehome>" /tmp/patch_p31335037/31335037/etc/config/actions.xml <oraclehome> <version>19.0.0.0.0</version> <min_version>19.0.0.0.0</min_version> <max_version>19.0.0.0.0</max_version> </oraclehome>确认<version>和<min_version>均为19.0.0.0.0。若显示19.10.0.0.0,说明你误下了 19.10 补丁包,必须换回 190000 后缀版本。这是新手最常踩的坑:看到补丁号相同就以为通用,结果 OPatch 报错Prerequisite check "CheckActiveFilesAndExecutables" failed,卡在预检阶段。
3. 打补丁前准备:停监听、关实例、配 OPatch,三步缺一不可
时区补丁虽不重启数据库,但要求$ORACLE_HOME下无活跃进程占用oracore/zoneinfo/timezone.dat文件。Linux 系统中,该文件被 Oracle 后台进程(如 PMON、DBW0)以只读方式映射进内存,若强行覆盖会导致进程异常终止。因此,必须先关闭数据库实例和监听器,再执行 OPatch。这不是 Oracle 官方文档的“建议”,而是 Linux 文件锁机制下的硬性约束。
3.1 确认当前 OPatch 版本 ≥ 12.2.0.1.24
Oracle 19c 要求 OPatch 最低版本为 12.2.0.1.24(2020 年 10 月发布)。低于此版本无法识别 19c 的补丁结构,会报错OPatch failed with error code 266。检查命令:
$ $ORACLE_HOME/OPatch/opatch version OPatch Version: 12.2.0.1.28 OPatch succeeded.若版本过低(如显示12.2.0.1.12),需先升级 OPatch:
- 从 MOS 下载最新 OPatch(补丁号
6880880) - 解压覆盖
$ORACLE_HOME/OPatch/目录(保留原目录权限) - 验证
opatch version输出 ≥12.2.0.1.24
注意:升级 OPatch 本身无需停库,但必须确保
$ORACLE_HOME下无其他 OPatch 进程运行(如ps -ef | grep opatch)。
3.2 关闭数据库实例与监听器
以 oracle 用户执行:
$ sqlplus / as sysdba << 'EOF' SHUTDOWN IMMEDIATE; EXIT; EOF $ lsnrctl stop LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 02-APR-2024 09:30:15 Copyright (c) 1991, 2019, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521))) The command completed successfully关键验证点:执行ps -ef | grep -E "(ora_|tnslsnr)"应无任何 Oracle 进程输出。若有残留(如ora_pmon_<SID>),说明SHUTDOWN IMMEDIATE未完全生效,需强制kill -9进程后再试。这是后续打补丁失败的头号原因——进程未彻底退出导致文件被锁,OPatch 报错OUI-67073: Unable to lock Central Inventory。
3.3 设置环境变量并验证补丁可读性
确保当前 shell 已正确设置ORACLE_HOME、ORACLE_SID、PATH:
$ echo $ORACLE_HOME /u01/app/oracle/product/19c/dbhome_1 $ echo $ORACLE_SID orcl $ which opatch /u01/app/oracle/product/19c/dbhome_1/OPatch/opatch然后验证 OPatch 能正确读取补丁包:
$ $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir /tmp/patch_p31335037/31335037 Oracle Interim Patch Installer version 12.2.0.1.28 Copyright (c) 2023, Oracle Corporation. All rights reserved. PREREQ session Oracle Home : /u01/app/oracle/product/19c/dbhome_1 Central Inventory : /u01/app/oraInventory from : /u01/app/oracle/product/19c/dbhome_1/oraInst.loc OPatch version : 12.2.0.1.28 OUI version : 12.2.0.7.0 Log file location : /u01/app/oracle/product/19c/dbhome_1/cfgtoollogs/opatch/opatch2024-04-02_09-35-22AM_1.log Invoking prereq "checkconflictagainstohwithdetail" Prereq "checkConflictAgainstOHWithDetail" passed. OPatch succeeded.Prereq "checkConflictAgainstOHWithDetail" passed.表示补丁与当前$ORACLE_HOME无冲突,可安全应用。若此处失败(如报Prereq "checkConflictAgainstOHWithDetail" failed),常见原因是补丁包解压路径含中文或空格,或$ORACLE_HOME权限不足(需 oracle 用户对$ORACLE_HOME有 rwx 权限)。
4. 执行打补丁:OPatch apply + 静默模式避交互,一步到位
补丁应用过程本质是 OPatch 将files/oracore/zoneinfo/timezone.dat复制到$ORACLE_HOME/oracore/zoneinfo/,并更新$ORACLE_HOME/inventory/ContentsXML/oui-patches.xml记录。整个过程约 15 秒,但必须确保命令参数精准——尤其-silent模式能绕过所有交互式确认,适合脚本化部署。
4.1 使用 OPatch apply 命令安装补丁
$ $ORACLE_HOME/OPatch/opatch apply -silent -ocmrf /tmp/ocm.rsp /tmp/patch_p31335037/31335037 Oracle Interim Patch Installer version 12.2.0.1.28 Copyright (c) 2023, Oracle Corporation. All rights reserved. Oracle Home : /u01/app/oracle/product/19c/dbhome_1 Central Inventory : /u01/app/oraInventory from : /u01/app/oracle/product/19c/dbhome_1/oraInst.loc OPatch version : 12.2.0.1.28 OUI version : 12.2.0.7.0 Log file location : /u01/app/oracle/product/19c/dbhome_1/cfgtoollogs/opatch/opatch2024-04-02_09-40-15AM_1.log OPatch detects the Oracle Home as "/u01/app/oracle/product/19c/dbhome_1" Applying interim patch '31335037' to OH '/u01/app/oracle/product/19c/dbhome_1' Patching component oracle.rdbms, 19.0.0.0.0... Patching component oracle.rdbms.util, 19.0.0.0.0... Patching component oracle.oracore.rsf, 19.0.0.0.0... OPatch succeeded.参数说明:
-silent:静默模式,跳过所有Do you want to proceed? [y|n]确认,避免脚本卡住-ocmrf /tmp/ocm.rsp:指定 OCM(Oracle Configuration Manager)响应文件路径。若未配置 OCM,可创建空文件touch /tmp/ocm.rsp,OPatch 会忽略该参数;若省略此参数,OPatch 可能弹出交互式 OCM 配置向导,导致命令阻塞/tmp/patch_p31335037/31335037:补丁解压后的根目录(含etc/、files/子目录)
注意:命令末尾不能加
/斜杠(如/tmp/patch_p31335037/31335037/),否则 OPatch 会报错OPatch failed with error code 255,提示路径不存在。
4.2 验证补丁是否成功注册到 inventory
打补丁后,OPatch 会在$ORACLE_HOME/inventory/ContentsXML/oui-patches.xml中写入记录。快速验证方法:
$ $ORACLE_HOME/OPatch/opatch lsinventory -detail | grep -A 5 "31335037" Patch level status of the backup area: Patch level status of the central inventory: Patch level status of the Oracle Home: Patch details: Patch 31335037: applied on Tue Apr 02 09:40:22 CST 2024 Unique Patch ID: 24567890 Patch description: "Time Zone Update for 19.0.0.0.0"若输出中包含Patch 31335037: applied on ...及Time Zone Update for 19.0.0.0.0,说明补丁已成功注册。若lsinventory不显示该补丁,但opatch apply显示OPatch succeeded,大概率是 OPatch 缓存未刷新,执行$ORACLE_HOME/OPatch/opatch util cleanup清理缓存后重试。
4.3 检查 timezone.dat 文件是否已更新
补丁核心是替换timezone.dat。验证其修改时间与版本号:
$ ls -l $ORACLE_HOME/oracore/zoneinfo/timezone.dat -rw-r----- 1 oracle oinstall 1234567 Apr 2 09:40 /u01/app/oracle/product/19c/dbhome_1/oracore/zoneinfo/timezone.dat $ strings $ORACLE_HOME/oracore/zoneinfo/timezone.dat | head -5 TZDATA_VERSION=35 TZDATA_BUILD=2021a TZDATA_SOURCE=IANA TZDATA_URL=https://www.iana.org/time-zones TZDATA_LICENSE=Public DomainTZDATA_VERSION=35是关键标志。若仍显示TZDATA_VERSION=32,说明文件未被覆盖——常见原因是$ORACLE_HOME/oracore/zoneinfo/目录权限为只读(chmod 755),或 OPatch 运行用户非oracle(需chown oracle:oinstall $ORACLE_HOME)。此时不要手动 cp,必须重跑opatch apply,否则 inventory 与文件状态不一致,后续升级会出问题。
4.4 启动监听器与数据库实例
补丁应用完毕,按顺序启动:
$ lsnrctl start $ sqlplus / as sysdba << 'EOF' STARTUP; EXIT; EOF启动后立即验证V$TIMEZONE_FILE视图:
SQL> SELECT VERSION, CON_ID FROM V$TIMEZONE_FILE; VERSION CON_ID ---------- ---------- 35 0VERSION=35且CON_ID=0(CDB root)表示时区文件已生效。若此处仍显示VERSION=32,说明数据库启动时加载了旧缓存,需执行ALTER SYSTEM FLUSH SHARED_POOL;强制刷新共享池,再查一次。
5. 避坑指南:五个血泪经验总结,专治打补丁翻车现场
打 Oracle 补丁不是点下一步就行的图形化操作,尤其时区补丁这种“看不见摸不着”的底层文件更新,失败现象隐蔽、排查路径长。以下是我在某跨平台系统部署中踩过的坑,按发生频率排序,每条都附真实报错、根因和可抄作业的解决命令。
5.1 现象:OPatch 报错OPatch failed with error code 73
原因:补丁 ZIP 包下载不完整或校验失败,导致 OPatch 解析actions.xml时 XML 格式错误。
解决:
# 1. 重新下载补丁包 # 2. 校验 SHA256(见 2.2 节) # 3. 若校验失败,删除旧包,用 wget -c 续传(防网络中断) $ wget -c https://updates.oracle.com/Orion/Services/download/p31335037_190000_Linux-x86-64.zip?aru=24567890 -O p31335037_190000_Linux-x86-64.zip5.2 现象:opatch apply卡住不动,日志显示Waiting for the Oracle instances to shut down...
原因:数据库实例未完全关闭,后台进程(如ora_m000_<SID>)仍在运行,OPatch 检测到活动进程后无限等待。
解决:
# 1. 强制杀掉所有 Oracle 进程 $ ps -ef | grep ora_ | grep -v grep | awk '{print $2}' | xargs kill -9 # 2. 清理 IPC 资源(关键!) $ ipcs -mt | grep oracle | awk '{print $1}' | xargs -I {} ipcrm -m {} $ ipcs -st | grep oracle | awk '{print $1}' | xargs -I {} ipcrm -s {} # 3. 重试 opatch apply5.3 现象:lsinventory显示补丁已安装,但V$TIMEZONE_FILE.VERSION仍是 32
原因:数据库启动时从$ORACLE_HOME/oracore/zoneinfo/加载timezone.dat,但该目录下文件被其他进程(如备份脚本)锁定,OPatch 实际未写入新文件。
解决:
# 1. 检查文件是否真被更新 $ stat $ORACLE_HOME/oracore/zoneinfo/timezone.dat | grep Modify # 2. 若 Modify 时间早于打补丁时间,说明未更新,手动强制复制(仅应急) $ cp /tmp/patch_p31335037/31335037/files/oracore/zoneinfo/timezone.dat $ORACLE_HOME/oracore/zoneinfo/ $ chmod 640 $ORACLE_HOME/oracore/zoneinfo/timezone.dat # 3. 刷新数据库缓存 $ sqlplus / as sysdba << 'EOF' ALTER SYSTEM FLUSH SHARED_POOL; SELECT VERSION FROM V$TIMEZONE_FILE; EXIT; EOF5.4 现象:打补丁后数据库启动报错ORA-00600: internal error code, arguments: [kzldtGetTzVersion], [19], []
原因:补丁包与$ORACLE_HOME版本不匹配(如用 191000 包打 190000 数据库),导致内核函数调用参数错位。
解决:
# 1. 回滚补丁(必须用原 OPatch 版本) $ $ORACLE_HOME/OPatch/opatch rollback -id 31335037 # 2. 确认补丁已移除 $ $ORACLE_HOME/OPatch/opatch lsinventory | grep 31335037 # 应无输出 # 3. 下载正确版本补丁(190000 后缀),重试5.5 现象:应用补丁后,SELECT TZ_OFFSET('Asia/Shanghai') FROM DUAL;返回+07:00(应为+08:00)
原因:客户端 NLS_TIME_ZONE 设置错误,或应用连接字符串中指定了错误时区(如TIME_ZONE=GMT+7),与数据库时区文件无关。
解决:
-- 1. 查看当前会话时区 SQL> SELECT SESSIONTIMEZONE FROM DUAL; -- 2. 重置会话时区为数据库默认 SQL> ALTER SESSION SET TIME_ZONE = DBTIMEZONE; -- 3. 验证上海时区 SQL> SELECT TZ_OFFSET('Asia/Shanghai') FROM DUAL; TZ_OFFS ------- +08:006. 验证时区补丁是否真正生效:四步交叉验证法,一个都不能少
补丁打完不等于万事大吉。Oracle 时区行为涉及数据库内核、客户端驱动、操作系统三者协同,单点验证极易漏判。我给自己定了一套铁律:每次打完时区补丁,必须完成以下四步验证,缺一不可。这套方法在某金融级日志系统上线前救了我们两次——一次发现测试库打了补丁但生产库忘了打,另一次揪出 JDBC 驱动缓存了旧时区规则。
6.1 步骤一:查V$TIMEZONE_FILE确认内核版本
这是最直接的证据。执行:
SQL> COL VERSION FORMAT 999 SQL> COL CON_ID FORMAT 999 SQL> SELECT VERSION, CON_ID, FILENAME FROM V$TIMEZONE_FILE; VERSION CON_ID FILENAME ------- ------ ---------------------------------------- 35 0 /u01/app/oracle/product/19c/dbhome_1/oracore/zoneinfo/timezone.datVERSION=35且FILENAME路径指向$ORACLE_HOME下的timezone.dat,证明数据库内核已加载新版时区文件。注意CON_ID必须为0(CDB root),若为1或2,说明只在某个 PDB 生效,需检查 PDB 是否 open 并重复打补丁。
6.2 步骤二:测夏令时敏感时区(如Europe/London)
时区 35 的核心价值是修复 2021 年起夏令时规则。用Europe/London测试 3 月最后一个周日(夏令时开始日)的时间偏移:
SQL> ALTER SESSION SET TIME_ZONE = 'Europe/London'; SQL> SELECT 2 TO_CHAR(TIMESTAMP '2024-03-31 00:59:59', 'YYYY-MM-DD HH24:MI:SS TZR') AS before_dst, 3 TO_CHAR(TIMESTAMP '2024-03-31 01:00:00', 'YYYY-MM-DD HH24:MI:SS TZR') AS after_dst 4 FROM DUAL; BEFORE_DST AFTER_DST -------------------------- -------------------------- 2024-03-31 00:59:59 GMT 2024-03-31 02:00:00 BSTBST(British Summer Time,+01:00)正确出现,且01:00:00跳变为02:00:00,证明夏令时切换逻辑已激活。若AFTER_DST仍显示GMT,说明时区文件未生效,回溯检查V$TIMEZONE_FILE和timezone.dat文件内容。
6.3 步骤三:比对SYSTIMESTAMP与操作系统时间
数据库时间必须与 OS 时钟严格对齐,否则DBMS_SCHEDULER作业会漂移。在数据库中执行:
SQL> SELECT 2 SYSTIMESTAMP AS db_time, 3 TO_TIMESTAMP_TZ(TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM'), 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM') AS db_tz_time, 4 TO_TIMESTAMP_TZ(TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF') || ' ' || SUBSTR(TO_CHAR(SYSTIMESTAMP, 'TZH:TZM'),1,6), 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM') AS os_tz_time 5 FROM DUAL; DB_TIME DB_TZ_TIME OS_TZ_TIME ------------------------------ ------------------------------ ------------------------------ 2024-04-02 10:25:33.123456 +08:00 2024-04-02 10:25:33.123456 +08:00 2024-04-02 10:25:33.123456 +08:00三者毫秒级一致(DB_TIME、DB_TZ_TIME、OS_TZ_TIME完全相同),证明数据库时钟已同步 OS。若DB_TIME与OS_TZ_TIME差整数小时,说明ORACLE_HOME下lib/libnnz19.so或lib/libclntsh.so被旧版覆盖,需用adrci检查alert.log中Time drift detected告警。
6.4 步骤四:应用端实测(JDBC/ODBC 连接)
最后一步,也是最容易被忽略的:从应用服务器连上来查。在应用服务器(如 Tomcat 服务器)上执行:
$ java -cp ojdbc8.jar:. TestTz // TestTz.java 内容: import java.sql.*; public class TestTz { public static void main(String[] args) throws Exception { Connection c = DriverManager.getConnection("jdbc:oracle:thin:@host:1521:orcl", "user", "pwd"); ResultSet rs = c.createStatement().executeQuery("SELECT TZ_OFFSET('Asia/Shanghai') FROM DUAL"); rs.next(); System.out.println("Shanghai offset: " + rs.getString(1)); // 应输出 +08:00 } }若输出+08:00,说明 JDBC 驱动(ojdbc8.jar)与数据库时区协同正常。若输出+07:00,检查应用服务器 JVM 参数是否设置了-Duser.timezone=GMT+7,或连接字符串中是否硬编码了TIME_ZONE属性。
从那以后我每次给 Oracle 19c 打时区补丁,都强制走一遍这四步:V$TIMEZONE_FILE→Europe/London夏令时 →SYSTIMESTAMP对齐 → 应用端实测。少一步,上线后就可能收到凌晨三点的告警电话。希望帮到你。
本文还有配套的精品资源,点击获取