简介:Oracle 19c时区版本从32升级至42的实战资料,面向需要处理跨时区数据、在数据泵导入导出中遭遇TSTZ时区敏感时间戳报错的数据库管理员与运维人员。内容围绕时区版本演进、TSTZ数据类型特性及Data Pump兼容性问题,系统梳理了升级前后的典型报错原因,给出预处理数据、指定兼容模式、重新导出、调整数据泵参数等解决思路,并补充了备份、业务影响评估与升级后验证等最佳实践。压缩包共10个文件,其中6个dat时区数据文件用于加载新时区信息,2个xml配置文件辅助参数调整,2个txt说明文档提供操作指引,整体大小仅377KB,便于快速获取。已有1367人学习下载,适合Oracle DBA在时区升级或数据迁移时查阅参考。学习后可掌握时区升级标准流程、TSTZ报错排查方法,以及升级后的功能验证要点,确保全球业务场景下时间数据准确可靠。
1. 升级Oracle 19c时区版本32→42:数据泵导入TSTZ报错的解救方案
数据泵(expdp/impdp)导数据时碰到ORA-39401后面跟着一串错误日志,最底下挂着ORA-01882: timezone region not found,经历过的人都懂这个问题有多恶心。TSTZ(TIMESTAMP WITH TIME ZONE)类型的数据导不进去,不是数据坏了,也不是权限问题,而是源库的时区版本已经到42,目标库还停留在32,目标库压根认不出dmp文件里那些具名时区区域。这篇笔记完整记录了我把Oracle 19c时区版本从32升到42、解锁数据泵导入的过程,包括确认版本、跑升级脚本、四个真实踩坑点,以及一个不用升级也能临时导数据的绕过思路,适合正在做19c迁移或恢复的DBA直接参考。
2. 动手前先确认:当前时区版本和高低差决定了你要不要升级
时区版本就是一套zoneinfo区域数据文件的版本号,Oracle 19c刚装完默认是32,后续补丁可能把时区文件更新到34、40、42。数据泵导出的dmp文件会在元数据里记录时区版本,导出的TSTZ数据记录的是具名时区区域(比如Asia/Shanghai),当目标库时区版本低于源库时,数据库在目标库里找不到对应的时区区域名,就直接抛ORA-01882。我这次遇到的情况是源库时区版本42,目标库32,差了一个10。
2.1 三句话确认源库和目标库的时区版本
先别急着操作,一条SQL就能看到当前环境时区文件版本:
SELECT VERSION FROM V$TIMEZONE_FILE;这条命令返回一个数字,比如32、42。它读的是系统表里记录的时区文件版本,升级时区后这个数字会变化。如果目标库的VERSION小于源库,就说明TSTZ数据导入会出问题,需要把目标库的时区版本升上去。
再确认一下数据库层面的时区相关属性,看看里面存的属性值,方便后面对比升级前后是否有变化:
SELECT PROPERTY_NAME, PROPERTY_VALUE FROM DATABASE_PROPERTIES WHERE PROPERTY_NAME LIKE 'DST%';这里会看到类似DST_PRIMARY_TT_VERSION这样的属性,它表示数据库当前主时区转换表的版本号,升级成功后这里的值也会跟着变。两条SQL跑完,源库和目标库各跑一遍,对比结果就清楚了。
还有一个细节容易被跳过去:操作系统层面得有对应版本的时区数据文件。utltz.sql脚本执行时会把ORACLE_HOME下oracore/zoneinfo目录里的时区数据文件(比如timezdif_42.dat)加载进数据库,如果OS层面缺文件,脚本会直接失败。确认一下目标库的$ORACLE_HOME/oracore/zoneinfo/目录里有没有42版本的数据文件,没有就先补文件。
2.2 升级前必须做的备份与窗口准备
时区升级会替换数据库里的时区数据,影响范围是所有带TSTZ/TSTZ LTZ类型的数据和系统对象。升级前我一般强制走一遍全备,至少要把system表空间和sysaux表空间备份出来。为什么要强调这两个:时区数据文件表(SYSTEM表空间里的相关字典表)和系统对象(集中在SYSAUX)升级时都会被更新,万一脚本执行一半挂了,没有这两个表空间的备份,恢复起来特别被动。
窗口准备上,建议至少留30到60分钟。utltz.sql本身跑多久取决于数据量,通常10到20分钟能跑完,但升级后的重新编译(utlrp.sql)也得跑一阵子。操作之前把应用侧的连接停掉,尤其是不要有正在写入TSTZ数据的会话。我当时选择周五晚上操作,反正就是预留一个能接受停库或降级的窗口,别在业务高峰硬上。
3. 时区版本从32升到42:utltz.sql执行记录
时区版本升级的官方脚本是$ORACLE_HOME/rdbms/admin/utltz.sql,所有版本通用,19c也不例外。它做的事情本质上就是把新版本的时区数据文件读进数据库,并更新相关字典表,让数据库支持新的时区区域名和时间转换规则。
3.1 执行前打开联机升级开关
Oracle 19c支持联机升级时区版本,这是个很实用的特性。执行脚本之前,先用sysdba身份设置一个参数:
ALTER DATABASE SET TIMEZONE_VERSION_UPGRADE_ONLINE=TRUE;这个参数的意思是:允许在数据库运行状态下执行时区升级,不需要重启实例到upgrade模式。它能够正常工作靠的是数据库在升级过程中仍然能提供基础服务,但对TSTZ类型的操作会有限制。如果不开这个开关,那就要把数据库启动到upgrade模式,整个过程相当于做个停机维护。
打开开关后验证一下参数值,确认改成功了:
SELECT VALUE FROM V$PARAMETER WHERE NAME = 'timezone_version_upgrade_online';返回TRUE,就继续往下走。注意这个参数是静态数据库级别的,它控制的是时区升级脚本的运行方式,不是让所有TSTZ操作都无感,别把它理解成在线热迁移。
3.2 运行utltz.sql按提示确认切换
执行脚本前先把当前会话设置到sysdba,避免权限问题。然后执行:
SQL> @?/rdbms/admin/utltz.sql脚本开始后会打印当前时区版本和新的时区版本,提示输入TZ_VERSION。我这次环境里显示的是当前32,准备升级到42,根据提示输入:
TZ_VERSION=42然后脚本会继续跑,日志里能看到类似“Loading new timezone data”的信息。这里有个经验:不要另开会话去查V$TIMEZONE_FILE,升级过程中查到的版本号可能是中间状态,容易误导。脚本跑完没有报错就算第一步成功。整个执行时间不长,但后面还有一个隐藏步骤——重新编译旧时区依赖的包和存储过程。
重新编译用utlrp.sql:
SQL> @?/rdbms/admin/utlrp.sql这个脚本全名是utlrp(重新编译失效PL/SQL包),时区升级后,依赖旧时区数据的对象会被标记为INVALID,不重编译的话,后面调用这些对象的TSTZ操作还是会出问题,而且报错很奇特,让人误以为升级失败了。
3.3 升级后的验证:版本号、无效对象与时区信息
升级完先查版本号:
SELECT VERSION FROM V$TIMEZONE_FILE;这次返回42,和源库一致。再查一下时区区域数据是否真的加载进去了:
SELECT TZNAME, TZABBREV FROM V$TIMEZONE_NAMES WHERE TZNAME = 'Asia/Shanghai';能返回一行记录,说明具名时区已经被数据库识别了。这个验证特别关键,TSTZ导入报错的核心就是目标库缺这条数据。
最后看一下升级后的无效对象数量,确认utlrp有没有把所有对象编译干净:
SELECT COUNT(*) FROM DBA_INVALID_OBJECTS WHERE OWNER = 'SYS' AND OBJECT_TYPE IN ('PACKAGE', 'PACKAGE BODY');如果这个数量比升级前明显增加,说明有对象没编译成功,要单独挨个查。一般来说utlrp跑完能恢复正常。
升级完成后记得把联机开关改回FALSE,回到默认状态:
ALTER DATABASE SET TIMEZONE_VERSION_UPGRADE_ONLINE=FALSE;别小看这一步,跳过去之后下一次执行时区相关操作可能被数据库拒绝,而且报错信息看着像环境损坏,实际上是这个开关还开着。
4. 时区升级与数据泵恢复的避坑记录:四个真实踩坑点
这次升级过程中踩了四个坑,每个坑的教训都写在这里,按现象、原因、解决排列,方便你对照排查。
4.1 现象:utltz.sql执行时报ORA-01882,提示某个时区区域找不到
执行utltz.sql时脚本报ORA-01882,说region Asia/Shanghai not found。一开始我以为是时区数据文件没放对,折腾半天才发现原因:我是在一个旧的SQL*Plus会话里执行的,这个会话在数据库里已经缓存了旧时区版本的数据字典信息,脚本读取时用了过期的会话状态。
解决方式很简单:退出当前SQL*Plus会话,用sysdba重新登录,开一个全新的会话再跑。从那以后我执行utltz.sql前都会强制重连数据库,绝不在老会话里硬跑。
4.2 现象:升级脚本跑到一半卡住不动,日志也没有新输出
有一次执行utltz.sql,等了十几分钟日志一直没有变化,看起来像死掉了。原因是升级过程中有另外一个会话在并发执行一个大数据量的TSTZ类型查询,持有了一把内部锁,utltz.sql在等待这个锁释放。
解决方法是等待锁释放,或者找到阻塞会话把它杀掉。先查阻塞情况:
SELECT SID, SERIAL#, EVENT, BLOCKING_SESSION FROM V$SESSION WHERE BLOCKING_SESSION IS NOT NULL;找到阻塞源后,按SID和SERIAL#执行ALTER SYSTEM KILL SESSION杀掉它。升级时区这种操作,窗口内一定要确保没有其他TSTZ相关操作并发执行,我后来养成的习惯是升级窗口内让应用完全停掉,不让查询和导入任务跑到一半。
4.3 现象:升级完成后重新导入数据,TSTZ还是报ORA-01882
这是最迷惑人的一个坑。V$TIMEZONE_FILE已经显示42,但是impdp导数据时TSTZ还是报错。后来排查发现是dmp文件的元数据里,源库导入导出时使用了不同的时区参数,源库的数据库时区是带具名区域设置的,而目标库数据库时区是绝对值偏移量,两者不一致导致TSTZ列校验失败。
解决方法是先检查源库的数据库时区设置:
SELECT DBTIMEZONE FROM DUAL;如果源库是Asia/Shanghai这类具名区域,目标库也要保持一致。改目标库的数据库时区:
ALTER DATABASE SET TIME_ZONE = 'Asia/Shanghai';改完重启数据库,或者在允许的情况下重启数据库让设置生效(ALTER DATABASE SET TIME_ZONE需要重启才能生效),然后再跑impdp。这个坑说明升级时区版本并不等于完成全部配置,数据库本身的时间时区设置也必须匹配。
4.4 现象:utlrp.sql跑完仍然有大量INVALID对象
升级完做验证,发现DBA_INVALID_OBJECTS里SYS用户还有不少失效的包和存储过程。原因是utlrp.sql执行过程中,部分对象编译时依赖其他还没编译完成的对象,存在顺序依赖,一次跑不干净。
解决方法是反复执行utlrp.sql直到无效对象数量不再变化,或者单独编译剩余对象:
@?/rdbms/admin/utlrp.sql再查一遍:
SELECT COUNT(*) FROM DBA_INVALID_OBJECTS WHERE OWNER = 'SYS';我第一次没注意到这个细节,直接跑应用测试,结果应用报了一堆内部错误。从那以后凡是做utlrp,我都会循环跑两到三遍,直到无效对象数量完全稳定。
5. 数据泵导入落地验证:重新impdp与一个绕行方案
时区升级到42、数据库时区设置对齐后,重新执行之前的impdp导入命令:
impdp system/password@orcl \ directory=DATA_PUMP_DIR \ dumpfile=expdp_full_$(date +%Y%m%d).dmp \ logfile=impdp_restore.log \ parallel=4 \ table_exists_action=REPLACE这次没有再报TSTZ错误,日志文件末尾能看到“Job completed successfully”字样。针对TSTZ数据的验证,我通常会专门查一下导入后的时区数据是否真的可用:
SELECT COUNT(*) FROM TSTZ_TEST_TABLE WHERE ROW_NUM > 0;能正常返回行数,并且不报时区区域错误,就说明TSTZ数据已经落入目标库。
5.1 不能升级时怎么办:OFFSET时区绕行
现实里总会有目标库暂时不能升级的情况。有一个绕行办法:在源库把数据库时区设置从具名区域改成绝对偏移量,再重新导出。操作方式是:
ALTER DATABASE SET TIME_ZONE = '+08:00';这个做的本质是让TSTZ数据在导出时就以偏移量形式存储,不携带Asia/Shanghai这种具名区域名,目标库即使时区版本低,也能识别偏移量,因为偏移量不依赖时区区域文件。代价是会丢掉DST(夏令时)相关的历史信息,适合临时迁移或测试环境快速恢复,不适合作为长期方案。
如果这个会话不允许执行ALTER DATABASE,就用操作系统层设置TZ环境变量后重启实例。
5.2 一个实用习惯:数据泵迁移前先跑时区健康检查
现在我做数据泵迁移,第一步一定是先查源库和目标库的时区版本,以及各自的DBTIMEZONE设置。这两项如果有一个不匹配,TSTZ数据就一定会有问题。与其等到impdp报错再去排查,不如提前花两分钟把版本对比做掉。升级完后再把验证SQL全跑一遍,确认版本、时区区域、无效对象三个都正常再交给应用侧测试。从那以后我每次做数据泵迁移,都强制走一遍这个流程,再也没被TSTZ报错坑过。希望帮到你。
本文还有配套的精品资源,点击获取