news 2026/10/31 9:36:34

Oracle 19c时区35补丁p31335037升级与DBMS_DST实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Oracle 19c时区35补丁p31335037升级与DBMS_DST实战

简介:面向 Oracle 19c 数据库管理员与运维人员的时区补丁包,主要解决 ORA-39405 所反映的 TSTZ 版本不一致问题,可将 Oracle 19.3 环境的时区文件升级到 35 版本。需要留意,补丁仅适用于 19.3,其他 19c 版本误用会报错;描述中亦提到后续会提供覆盖全部 19c 版本的同类补丁。压缩包为 zip 格式,共 10 个文件,约 394KB,包含 6 个 dat 时区数据文件、2 个 xml 配置与元数据文件、2 个 txt 说明文件,体积轻量但组成完整,便于离线归档和版本核对。已有 2570 人学习,适合在变更前核对 v$timezone_file 显示的时区版本,并结合包内说明评估升级影响、排查 TSTZ 版本差异。包内说明文件有助于理解补丁用途与限制条件,降低版本错配风险;对于处理时区兼容性、准备 19.3 补丁验证与回退方案的 DBA,这份资料提供了可直接参考的补丁文件与基础说明。

1. 时区版本卡在 34 的那个下午:v35 补丁到底改了什么

某公司的月度报表对不上账,同一笔订单在结算系统显示 09:00,在报表平台显示 08:00。查了两天才定位到一句 SQL:SELECT * FROM v$timezone_file;返回的 VERSION 是 34,而新上的报表服务按最新时区规则换算。oracle19c 时区35 补丁 p31335037就是补这个口子的:它是 19.0.0 在 Linux x86-64 平台上的时区文件补丁,把 ORACLE_HOME 下的时区数据推到 v35,同时要求数据库里已有的TIMESTAMP WITH TIME ZONE数据跟着重算。

它解决的不是“报错”,而是“算错”。适合三类人:负责 19c 单实例或 RAC 运维的 DBA、做跨时区交易与报表的业务方、以及正打算用 Data Pump 在两边库之间搬数据的人。补丁本身不大,但它是少数“打完补丁还得再跑一遍数据升级”的类型,光opatch apply只能算做了一半。

2. 打补丁前的三张底牌:版本核对、冲突预检与备份清单

时区补丁翻车的重灾区不在 apply 那几分钟,而在 apply 之前没把现状摸清。我一般会在停机窗口前一周就把下面三件事做完,把该确认的版本、该留的文件、该记的清单都落到纸面,窗口里只做“执行 + 判读”,不做“探索”。

2.1 用 v$timezone_file 和 database_properties 定位现状

先分清两个“版本”:一个是 ORACLE_HOME 里时区文件的版本,一个是数据库自己记的时区版本。这两者不一致,就是后面 ORA-39405 的根子。下面三条 SQL 建议在补丁前、补丁后各跑一次,输出存档。

-- 1) ORACLE_HOME 装载的时区文件版本,正常应为 timezlrg_35.dat / 35 SELECT * FROM v$timezone_file; -- 2) 数据库层面的时区属性,重点看 DST_UPGRADE_STATE SELECT property_name, property_value FROM database_properties WHERE property_name IN ('DBTIMEZONE', 'TIME_ZONE', 'DST_UPGRADE_STATE'); -- 3) 找出存量 TSTZ 列,决定要不要走 DBMS_DST SELECT owner, table_name, column_name, data_type FROM dba_tab_cols WHERE data_type IN ('TIMESTAMP WITH TIME ZONE', 'TIMESTAMP WITH LOCAL TIME ZONE') AND owner NOT IN ('SYS','SYSTEM','XDB','MDSYS','CTXSYS','ORDSYS','WMSYS', 'LBACSYS','DVSYS','AUDSYS','GSMADMIN_INTERNAL') ORDER BY owner, table_name;

第一条的FILENAME指向$ORACLE_HOME/oracore/zoneinfo/下的实际文件,VERSION就是当前生效的时区版本,34 或 35 一眼可见。第二条里DST_UPGRADE_STATE是重点,取值NONE说明干净,出现PREPARE、UPGRADE、COMMIT说明上一次升级没走完,必须先收尾再打新补丁,否则实例启动会卡在 pending 状态,这个坑我在第 5 章细说。第三条决定了工作量:返回 0 行,补丁打完启动就结束;返回几十上百行,就得排 DBMS_DST 的窗口。

提示:第三条 SQL 别只查业务用户,dba_tab_cols里的INTERNAL_USE列可以过滤掉系统内部表,但业务 schema 一定要全量看,漏掉一张分区表就可能在升级阶段被错误表打回来。

2.2 OPatch 预检:冲突、空间与 zip 完整性

时区补丁是 interim patch,走标准 OPatch 流程,但它对 OPatch 自身版本有最低要求,补丁包里的 readme 会写明。预检阶段我固定跑四条命令,任何一条不过就不进窗口。

export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 export PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch:$PATH # 1) OPatch 版本是否满足补丁要求 opatch version # 2) 当前 home 已装的补丁,确认 31335037 没打过、也没冲突补丁 opatch lsinventory -bugs_fixed | grep -E '31335037|31771877' # 3) 正式的冲突预检,-phBaseDir 指向解压后的补丁目录 opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir /u01/patch/dst35/31335037 # 4) zip 完整性校验,只做 CRC 不做解压 unzip -t /u01/software/p31335037_190000_Linux-x86-64.zip

第 2 条里如果31335037已经出现在库存中,再次 apply 会直接报“Patch already applied”,这时候要确认是“打过但没做数据升级”还是“打完回退过”,两种情况处理方式完全不同。第 3 条的-phBaseDir参数容易写错,它要的是解压后的目录,不是 zip 文件本身;写成 zip 会报路径不存在,很多人第一次都会栽在这。第 4 条是血泪经验:补丁包从内网传到生产环境,中途断点续传导致 CRC 损坏的情况我遇到过两次,apply 到一半报解压失败,窗口就白开了。

2.3 备份清单:RMAN 之外还得留哪几个文件

时区补丁的回退分两层:OPatch 层能 rollback 文件,数据层只能靠备份。所以备份清单要比普通补丁多留几样东西,下面这张表是我沿用至今的模板。

对象路径或命令用途
时区数据文件$ORACLE_HOME/oracore/zoneinfo/整目录文件级回退,比 rollback 更快
OPatch 库存快照opatch lsinventory -detail > lsinv_before.txt对比 apply 前后差异
数据库全备rman backup database plus archivelog数据层后悔药,COMMIT 之后唯一退路
参数文件create pfile='/backup/pfile_before.ora' from spfile;排除参数被意外改动
DST 系统表sys.dst$affected_tables、sys.dst$error_table结构升级中断时判断进度

时区文件的整目录备份最容易被忽略,因为很多人觉得“OPatch 能 rollback 就够了”。但 rollback 只回退 ORACLE_HOME 的库存记录和文件,已经用 DBMS_DST 重算过的 TSTZ 数据它管不了;而直接拿备份目录覆盖回去,至少能让实例先起来,再决定要不要走数据回退。这两条路的边界,第 6 章会展开。

3. 停机窗口里应用 p31335037:命令序列与输出判读

窗口里的动作要短、要可判读。我习惯把整个流程写成三段:停库前准备、apply、启动后验证,每一段都有明确的“继续 / 中止”信号,而不是从头跑到尾再回头找日志。

3.1 unzip 到 opatch apply 的完整流程

单实例和 RAC 的差别主要在停库方式,apply 本身是一样的。下面按单实例写,RAC 在注释里标出差异。

export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 export PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch:$PATH # 1) 准备解压目录,避免和别的补丁混在一起 mkdir -p /u01/patch/dst35 && cd /u01/patch/dst35 unzip /u01/software/p31335037_190000_Linux-x86-64.zip ls -l /u01/patch/dst35/31335037/ # 2) 停库。RAC 环境改用:srvctl stop database -d ORCL sqlplus / as sysdba <<'EOF' shutdown immediate; exit EOF # 3) 应用补丁,路径指向解压后的补丁号目录 opatch apply /u01/patch/dst35/31335037

第 1 步解压后一定要ls看一眼目录结构,正常应该能看到etc/、files/和README.txt,如果只有一层嵌套目录(比如31335037/31335037/),说明打包方式和预期不同,apply 时要指到内层。第 3 步的输出判读比命令本身重要:看到OPatch succeeded才算过;出现Prerequisite check "CheckActiveFilesAndExecutables" failed,说明还有进程占着 ORACLE_HOME 里的库文件,多半是监听或某个后台进程没停干净,ps -ef | grep $ORACLE_HOME查一遍再重来。

RAC 环境要特别注意:时区文件装在每个节点的 ORACLE_HOME 里,不是共享存储上的同一份。所以必须每个节点都 apply,只打一个节点是最典型的半打状态,第 5 章会讲它引发的现象。

3.2 startup 之后看什么:alert log 与 v$timezone_file

补丁打完,启动实例,这一刻的验证决定后面要不要排 DBMS_DST 的窗口。

sqlplus / as sysdba <<'EOF' startup SELECT * FROM v$timezone_file; SELECT property_name, property_value FROM database_properties WHERE property_name = 'DST_UPGRADE_STATE'; EOF # alert log 里筛时区相关记录 grep -iE 'time ?zone|DST|tz' \ $ORACLE_BASE/diag/rdbms/orcl/orcl/trace/alert_orcl.log | tail -50

判读标准有三条。第一,v$timezone_file的VERSION变成 35、FILENAME变成timezlrg_35.dat,说明 ORACLE_HOME 层生效了。第二,DST_UPGRADE_STATE正常是NONE;如果变成UPGRADE_PENDING或UPGRADE,说明数据库层认为还有数据要处理,这时候别急着开业务。第三,alert log 里出现Timezone version mismatch或 ORA-39405 字样,和第二条是同一方向的信号,指向“文件升级了、数据没升级”。

注意:启动后如果实例报 ORA-39405 起不来,不要第一反应去 rollback 补丁。常见做法是先进startup upgrade,把 DBMS_DST 流程跑完再正常启动,rollback 反而会把问题复杂化,具体在 5.1 展开。

3.3 无 TSTZ 数据的库,走一条快路径

2.1 第三条 SQL 返回 0 行、且没有物化视图或函数索引引用 TSTZ 表达式时,这个库可以走快路径:补丁 apply 完、实例正常启动、v$timezone_file显示 35、DST_UPGRADE_STATE为NONE,就结束了,不需要 DBMS_DST。

稳妥起见我还会跑一次只读检查脚本,它会把库里所有可能受时区版本影响的对象列出来,不修改任何数据:

sqlplus / as sysdba @?/rdbms/admin/utltz_upg_check.sql

这个脚本的输出会写到当前目录的utltz_upg_check.log,重点看有没有TSTZ相关的表被列出来。如果有,说明 2.1 那条 SQL 的过滤条件漏了对象(比如某些系统组件建的内部表),还是老老实实进第 4 章。快路径的前提是“真的没有存量数据”,不是“我以为没有”。

4. DBMS_DST 全流程:把存量 TSTZ 数据迁到 v35

DBMS_DST 是时区数据升级的核心包,它的设计是“两阶段 + 可回滚”:准备阶段只扫元数据、不动数据;升级阶段才真正重算,且中间可以 rollback。整个流程有四五个固定步骤,顺序错了就得从头来,所以下面按可复制的顺序写。

4.1 BEGIN_PREPARE 与 FIND_AFFECTED_TABLES 的正确用法

准备阶段的目的是把“哪些表的哪些列会受影响”列出来,让业务方有机会先评估,也让你自己知道要留多大窗口。

-- 1) 进入准备阶段,只生成元数据,不改数据 EXEC DBMS_DST.BEGIN_PREPARE(35); -- 2) 把受影响表清单落成一张本地表,方便前后比对 BEGIN EXECUTE IMMEDIATE 'DROP TABLE tz_affected PURGE'; EXCEPTION WHEN OTHERS THEN NULL; END; / CREATE TABLE tz_affected AS SELECT * FROM TABLE(DBMS_DST.FIND_AFFECTED_TABLES(35, 'n')); -- 3) 看一眼清单规模,估算窗口 SELECT COUNT(*) FROM tz_affected; -- 4) 准备阶段结束 EXEC DBMS_DST.END_PREPARE;

BEGIN_PREPARE的参数是目标时区版本 35,不是当前版本,写错会直接报错。FIND_AFFECTED_TABLES有两个参数:第一个是目标版本,第二个是'n'或'y',表示是否并行扫描;生产库上我一般用'n',避免扫描期间把 IO 打满影响业务。返回的列里OWNER、TABLE_NAME、COLUMN_NAME是关键,ERROR_COUNT不为 0 的条目要单独看。

准备阶段结束后,DST_UPGRADE_STATE会短暂变成PREPARE,END_PREPARE之后回到NONE。如果这一步之后状态还是PREPARE,说明END_PREPARE没执行成功,别往下走。

4.2 BEGIN_UPGRADE、错误表与 COMMIT_DST_UPGRADE

升级阶段是真正动数据的一步,也是窗口里最耗时的部分。它的机制是给受影响表加内部触发器,把旧规则下的 TSTZ 值按新规则重算,算错的记录落到sys.dst$error_table。

-- 1) 进入升级阶段,开始重算存量数据 EXEC DBMS_DST.BEGIN_UPGRADE(35); -- 2) 查错误表和进度 SELECT COUNT(*) FROM sys.dst$error_table; SELECT * FROM sys.dst$error_table WHERE ROWNUM <= 20; -- 3) 错误可修复时,先清洗脏数据再重跑 -- 常见错误:ORA-01858(非数字字符)、ORA-01841(年份越界) -- 4) 验证通过后提交,数据才算真正落盘 EXEC DBMS_DST.COMMIT_DST_UPGRADE; -- 5) 提交后结束升级阶段 EXEC DBMS_DST.END_UPGRADE(35);

第 1 步的BEGIN_UPGRADE会给表加锁并创建内部触发器,业务在这期间写入 TSTZ 数据也会走新规则,所以窗口内最好把相关表设为只读或停业务,否则升级和业务写入混在一起,错误表会更难看。第 4 步的COMMIT_DST_UPGRADE是分水岭:提交之前可以用ROLLBACK_DST_UPGRADE退回去,提交之后就只能靠 RMAN 备份了。

错误表里的记录不要直接删。常见做法是先把出错的行导出来,定位是数据本身有问题(比如年份写成 0000、时区名拼错)还是规则变更导致,修完数据后用DBMS_DST.SYNC_DST_UPGRADE同步,再重新检查错误表。整个过程可以反复迭代,只要没 COMMIT,数据都还能回退。

4.3 用 SQL 抽样验证偏移,别只看状态位

DST_UPGRADE_STATE回到NONE只说明流程走完了,不说明结果对。v35 改的是一些地区的历史夏令时和偏移规则,最可靠的验证是拿已知变更日期做断言。

-- 取一个受规则变更影响的时区,比对 UTC 换算结果 SELECT TZ_OFFSET('America/Sao_Paulo') AS offset_now, FROM_TZ(TIMESTAMP '2020-01-01 00:00:00', 'America/Sao_Paulo') AT TIME ZONE 'UTC' AS utc_conv FROM dual; -- 从受影响表里抽样,和业务方提供的基准值对比 SELECT id, create_time, create_time AT TIME ZONE 'UTC' AS utc_time FROM biz_order WHERE create_time >= TIMESTAMP '2020-01-01 00:00:00 +08:00' AND ROWNUM <= 20;

第一段 SQL 的TZ_OFFSET返回当前时区偏移,FROM_TZ ... AT TIME ZONE做一次换算,两个值和你手上的时区表对照,能快速判断规则有没有生效。第二段是抽样比对,把升级前后的utc_time各存一份,差异行数应该和tz_affected的规模对得上。

验证通过后再开业务,顺序别反。我见过先开业务、后跑验证的,结果业务写入的新数据和升级中的触发器打架,错误表里混进一批本来不该有的记录,最后只能整表重来。

5. 避坑与排查:五个高频翻车现场

时区补丁的坑高度集中,翻来覆去就是那几类。下面五条按“现象 → 原因 → 解决”写,都是窗口里真会碰到的。

5.1 补丁打完实例起不来,报 ORA-39405

现象:opatch apply成功,startup时报 ORA-39405,实例停在 mount 或 open 阶段起不来。原因:ORACLE_HOME 的时区文件已经是 35,但数据库层记的版本还是 34,且库里存在 TSTZ 数据,启动校验不通过。解决:用startup upgrade进实例,跑完 4.1 到 4.2 的 DBMS_DST 流程,再shutdown immediate正常启动;不要在这个节点 rollback 补丁,rollback 会让文件版本退回 34,和已经部分升级的数据状态更难对齐。

5.2 RAC 只打了部分节点,连接时快时慢

现象:同一个应用,连节点 1 正常,连节点 2 报时区相关错误,或者两边查出来的 TSTZ 值差一小时。原因:时区文件在各自节点的 ORACLE_HOME 下,不是共享存储,只 apply 了一个节点就形成半打状态。解决:停整个数据库,在所有节点分别opatch apply,确认opatch lsinventory在每个节点都能看到 31335037,再统一启动。查的时候用srvctl status database -d ORCL -v配合各节点的v$timezone_file对比,比看日志快。

5.3 Data Pump 导入报 ORA-39361 或 ORA-39405

现象:expdp 正常,impdp 到目标库时报时区版本不兼容,导入中断。原因:源库已经升到 35,目标库还是 34,Data Pump 对 TSTZ 数据的校验过不去。解决:先给目标库打同一个补丁并完成数据升级,再做导入;impdp的VERSION参数只控制对象版本,不解决时区文件版本差异。迁移前固定动作是对两边库各跑一次SELECT * FROM v$timezone_file;,版本不一致就先暂停。

5.4 物化视图刷新失败或查询改写失效

现象:升级后 mview 定时刷新报错,或者执行计划里原本的查询改写没了。原因:mview 定义里引用了 TSTZ 表达式,DBMS_DST 升级过程中相关对象被置为失效,COMMIT_DST_UPGRADE不会自动恢复。解决:升级前用dba_mviews和dba_dependencies把依赖 TSTZ 列的 mview 列出来存档,升级完成后逐个ALTER MATERIALIZED VIEW ... COMPILE,再确认QUERY REWRITE状态;函数索引同理,需要重建。

5.5 DBMS_DST 卡在 PREPARE 状态,重跑报错

现象:上一次升级中断,DST_UPGRADE_STATE一直是PREPARE,再次执行BEGIN_PREPARE报“操作不允许”。原因:BEGIN_PREPARE之后没有执行END_PREPARE,或者BEGIN_UPGRADE失败后没回滚,系统表sys.dst$*里留着中间状态。解决:先执行EXEC DBMS_DST.END_PREPARE;收尾,如果还不行就EXEC DBMS_DST.ROLLBACK_DST_UPGRADE;回到干净状态再重来;sys.dst$系列表是 DBMS_DST 的内部结构,不要手工 DELETE,手工清理会把状态机搞得更乱。

6. 回退与灰度:一次时区升级的可复现流程

回退这件事,得先把边界说清楚。OPatch 的 rollback 能退回 ORACLE_HOME 里的.dat文件和库存记录,但退不回已经被 DBMS_DST 重算过的 TSTZ 数据。所以真正有效的后悔药分两段:数据升级阶段用ROLLBACK_DST_UPGRADE,提交之后用 RMAN 全备。搞混这两段,是时区补丁最容易出的事故。

# OPatch 层回退:只回退文件和库存,不碰数据 opatch rollback -id 31335037 # 回退后确认版本退回 34 sqlplus / as sysdba <<'EOF' SELECT * FROM v$timezone_file; EOF

rollback -id后面跟的是补丁号,不是 zip 文件名。执行完v$timezone_file应该退回 34,但这时候如果数据库层还是 35、表里又是新规则的数据,实例大概率起不来,所以 rollback 只适合“apply 完还没跑 DBMS_DST”的场景。已经 COMMIT 过的库,回退路径只能是 RMAN 恢复到补丁前的备份,没有捷径。

我现在的习惯是把整个升级压成一张检查表,窗口里逐项打勾,不靠记忆:

阶段检查项命令或依据通过标准
前置时区文件版本SELECT * FROM v$timezone_file;VERSION = 34
前置升级状态干净DST_UPGRADE_STATE值为 NONE
前置补丁未安装opatch lsinventory -bugs_fixed无 31335037
应用补丁执行结果opatch apply输出OPatch succeeded
应用文件版本v$timezone_filetimezlrg_35.dat / 35
数据准备阶段BEGIN_PREPARE/END_PREPARE状态回到 NONE
数据错误表sys.dst$error_table计数为 0 或已清洗
数据提交COMMIT_DST_UPGRADE无报错
验证抽样换算TZ_OFFSET/AT TIME ZONE与基准值一致
验证对象状态dba_objects.status无 INVALID 业务对象

这张表最大的价值不是“防漏”,而是“防提前”。很多人卡在“补丁 apply 成功就以为结束了”,结果DST_UPGRADE_STATE还挂着UPGRADE_PENDING,业务一接进来就开始报错。从那以后我每次做时区升级,都强制在开业务之前把最后两行验证跑完,验证不过就不解除只读,宁可多停十分钟,也不要在业务高峰去查错误表。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 9:36:01

深入解析synchronized底层原理:从对象头到锁升级与内存屏障

1. 从一个仓库路径说起&#xff1a;这个标题到底在讲什么 第一次看到这个标题&#xff0c;很多人会愣一下—— java hdporn,docs/java/concurrent/SynBottom.md wt1814/wt-note - Gitee.com &#xff0c;这串东西看起来像是把仓库地址、文件路径和某个关键词硬拼在了一起。我…

作者头像 李华
网站建设 2026/10/31 9:35:38

Rancher 2.11到2.12升级问题解析与解决方案

1. Rancher 2.11到2.12升级核心问题解析 最近在帮客户做Rancher 2.11到2.12的版本升级时&#xff0c;遇到了几个典型问题&#xff1a;审计日志异常、集群状态不一致和界面缓存问题。这些问题看似独立&#xff0c;实则都与Rancher的架构变更和数据处理机制有关。 审计日志在2.1…

作者头像 李华
网站建设 2026/10/31 9:35:36

MySQL JSON数据类型应用与优化全指南

1. JSON数据类型在MySQL中的核心价值 MySQL 5.7版本引入的JSON数据类型&#xff0c;彻底改变了传统关系型数据库处理半结构化数据的方式。与直接将JSON存储为TEXT或VARCHAR类型相比&#xff0c;专门的JSON数据类型具有三大不可替代的优势&#xff1a; 首先&#xff0c;存储引擎…

作者头像 李华
网站建设 2026/10/31 9:35:21

SpringBoot+Redis构建博物馆高并发票务系统实践

1. 项目背景与核心需求 恐龙博物馆门户景点系统是一个典型的文博类数字化平台&#xff0c;其核心目标是解决传统博物馆信息化程度低、游客服务单一的问题。这类系统通常需要处理高并发访问、多媒体内容展示、票务管理等多维度需求。 我在实际开发中发现&#xff0c;采用Spring…

作者头像 李华
网站建设 2026/10/31 9:34:14

风电功率预测实战:LightGBM在小样本高噪声时序数据中的应用

简介&#xff1a;本资源是一套基于LightGBM机器学习模型的风电功率预测完整实践项目&#xff0c;面向计算机、自动化、新能源等相关专业的本科生与研究生&#xff0c;适用于毕业设计、课程设计及期末大作业等教学实践场景。项目提供从数据预处理、模型训练&#xff08;train.py…

作者头像 李华
网站建设 2026/10/31 9:34:11

阶梯型矩阵实战:判定条件、高斯消元与数值稳定性

开头先抛一个我前阵子遇到的事。给一批实验数据做多元线性拟合&#xff0c;代码跑完系数看着挺正常&#xff0c;可残差平方和比预估的大了一个数量级。查了大半天&#xff0c;问题不在数据预处理&#xff0c;也不在优化器&#xff0c;而是我用来判断设计矩阵是否满秩的那段阶梯…

作者头像 李华