大家好,这里是 Lucifer三思而后行,专注于提升数据库运维效率。
目录
- 前言
- 环境安装
- 问题重现
- 时区检查
- 修改时区
- 问题分析
- 问题解决
- 深究根源
- 问题一
- 问题二
- 问题三
- 写在最后
- 往期精彩文章推荐
前言
昨天遇到一个问题,Oracle RAC 安装完之后,客户反馈数据库查询的时间不对,经分析原来是系统时区与客户所属时区不一致(看来是安装操作系统时选错了时区),需要修改系统时区。
本以为是一个很简单的操作,没想到踩坑了,修改系统时区之后,查询发现以下问题(已解决):
于是把问题重现了一下进行分享,希望大家能够在遇到这种情况时可以及时避坑。
环境安装
首先用 Oracle一键安装脚本 快速部署一套测试环境:
耗时 45 分钟左右,下面重现下问题。
问题重现
时区检查
当前环境我的系统时区是 Asia/Shanghai (CST, +0800)
:
查看数据库时间:
修改之前可以发现两个方式查询的时间和时区是一致的。
修改时区
首先停止数据库以及集群服务:
手动修改操作系统时区:
可以发现时区已经修改成功,但是为了确保起见,重启一下两台服务器主机:
重启完成后,再次检查操作系统时区:
确保没有问题后启动集群和数据库:
正常启动:
验证一下修改后的数据库时间:
问题成功复现了。
问题分析
通过以上情况可以发现,本地的时区是正确的,但是使用 TNS 连接的时区是不对的,因为通过 TNS 连接数据库查询 SYSTIMESTAMP 是取的监听时间,也就是说监听可能存在问题。
这里可以参考 MOS 文档中的 point 9 & 11: Dates & Calendars - Frequently Asked Questions (Doc ID 227334.1)
检查监听的时间是否正确:
通过上面的输出可以发现一个很诡异的现象:
为什么监听启动时间会是错误的呢?为了尽快解决问题,通过搜索 MOS 发现了一个文档有相关解决方案: How To Change Timezone for Grid Infrastructure (Doc ID 1209444.1)
其中有一段描述与我们遇到的情况极其相似:
可以看到从 11.2.0.2 版本开始,TZ 时区环境变量需要读取 $GRID_HOME/crs/install/s_crsconfig_<nodename>_env.txt
文件中的 TZ
变量值,这也会影响监听的启动时间。
检查文件中对应的 TZ 变量值:
当前配置文件的 TZ 值明显为修改之前的时区,看来是符合的。
问题解决
既然知道了解决方案,那解决就很简单了,修改 $GRID_HOME/crs/install/s_crsconfig_<nodename>_env.txt
文件中的 TZ 变量为正确的值即可:
修改后需要重启数据库和集群才能生效,最好重启主机:
重启后检查监听:
可以看到监听时间和系统时间已经保持一致,再次验证数据库时间:
时区一致,与客户确认后,已恢复正常。
深究根源
问题解决,就结束了吗?不,还想深究下 3 个问题:
- 这个 txt 配置文件是如何生成的?
- txt 配置文件中的 TZ 值是如何获取的?
- 监听为什么会受这个文件影响?
问题一
这个配置文件是什么时候生成的?通过分析 Grid 安装日志,大致算是搞清楚了,下面记录一下分析过程,通过倒退的方式。
首先在 $GRID_HOME/crs/install/s_crsconfig_lib.pm
文件中有一个函数:
我这里截取了其中重要的一部分,也就是创建 s_crsconfig_$HOST_env.txt
文件的代码,这段代码的主要目的是生成一个环境变量配置文件 s_crsconfig_<HOST>_env.txt
,其中包含头注释信息,并且会写入 TZ(时区)变量的值。
当然,上面只是定义了一个函数,并没有在这个文件中被调用,调用函数的文件是 $GRID_HOME/crs/install/crsconfig_lib.pm
,对应的代码段为:
顺藤摸瓜,找到调用 run_env_setup_modules
函数的文件是 $GRID_HOME/crs/install/rootcrs.pl
,对应的代码块是:
那么 rootcrs.pl
文件是在哪里以及什么时候调用的呢?对 root.sh 执行过程比较熟悉的朋友应该已经知道了,不熟悉的朋友也没关系,继续顺藤摸瓜就行。
调用 rootcrs.pl
的文件是 $GRID_HOME/crs/config/rootconfig.sh
,对应的代码块是:
接着就是 rootconfig.sh
被 $GRID_HOME/root.sh
文件调用,对应的代码块是:
到这里,生成 s_crsconfig_$HOST_env.txt
文件的整个流程就很清晰了,大概如下:
- 安装 Grid 时执行
$GRID_HOME/root.sh
- root.sh 脚本调用
$GRID_HOME/crs/config/rootconfig.sh
脚本 - rootconfig.sh 脚本调用
$GRID_HOME/crs/install/rootcrs.pl
脚本 - rootcrs.pl 脚本调用
$GRID_HOME/crs/install/crsconfig_lib.pm
文件 - crsconfig_lib.pm 文件调用
$GRID_HOME/crs/install/s_crsconfig_lib.pm
文件 - s_crsconfig_lib.pm 文件中定义了函数创建
s_crsconfig_$HOST_env.txt
文件。
第一个问题算是彻底研究明白了。
问题二
因为前面知道 s_crsconfig_$HOST_env.txt
文件的创建函数了,所以只要看下创建时 TZ 值是如何获取就知道了。
查看创建函数获取 TZ 变量值的代码段:
这段代码的主要作用是检查配置对象 $CFG
中是否定义了 TZ 参数,并在存在时获取其值,去除其中的单引号,然后将其以 TZ=<value>
的格式写入到文件中。
也就是说需要知道 $CFG
对象是如何定义的,这个对象是在 $GRID_HOME/crs/install/rootcrs.pl
脚本中定义的,对应的代码段是:
可以看到获取参数值的对应文件为当前目录下的 crsconfig_params
文件,查看文件中是否存在 TZ 相关的参数:
📢 注意:这里的 TZ 值还是 Asia/Shanghai,为什么没有影响到数据库的运行?因为这里仅用于安装时生成
s_crsconfig_$HOST_env.txt
文件所用,所以不修改也无关大雅。
这个文件是由 crsconfig_params.sbs
生成,其中 TZ 参数:
可以看到 TZ 变量值是通过 %oracle_install_crs_Timezone%
获取。
在 Grid 安装过程中会生成一个静默文件 $GRID_HOME/inventory/response/oracle.crs_Complete.rsp
,其中记录了 Grid 安装时获取到的 %oracle_install_crs_Timezone%
值:
这个文件的值是来源于 $GRID_HOME/inventory/globalvariables/oracle.crs/globalvariables.xml
,对应的安装日志中也有相关记录:
TZ 的值是通过 /soft/grid/install/../stage/globalvariables/globalvar.xml
文件读取的,所以只需要搞清楚 globalvar.xml 的值是如何注入的就知道了。
这个可以在安装软件解压后的文件中找到相关定义:
在软件包刚解压时,oracle_install_crs_Timezone 对应的 VALUE 值为空,在安装过程中被写入,至于如何写入的,这个在安装日志中没有找到,怀疑可能是核心 jar 包中获取写入的。
问题三
至于最后一个问题,监听为什么会受这个文件影响?这个就需要追溯到监听是在哪一步启动了,在前几天写过一篇: Oracle RAC 集群启动顺序 就有这个答案:
监听是由 oraagent
服务启动,所以需要查看 bin/oraagent
脚本文件,对应的代码段为:
这个文件是为了找 oraagent.bin 执行,看起来并没有相关 TZ 配置文件的调用,但是在脚本中我发现了:
由此看来,这个文件是在调用 ohasd
时调用生效,而 ohasd 是最初启动的进程:
查看 $GRID_HOME/log/rocky9-01/client/crswrapexece.log
日志也可以看到:
这样的话,一切就说的通了,在 ohasd 服务启动时,会去获取 s_crsconfig_${MY_HOST}_env.txt
文件作为环境变量文件 ENV_FILE,然后生效该环境变量文件,自然也就生效了 TZ 时区变量,后续启动监听时也会读取 TZ 变量值。
写在最后
虽然还是有一些疑问❓没有得到解答,比如 TZ 到底是如何从操作系统层面获取并赋值的?但是通过这一次深挖,确实对 Grid 的安装启动流程了解更加清晰了,希望能对大家也有所帮助!
感谢您的阅读,这里是 Lucifer三思而后行,欢迎 点赞+关注,我会持续分享数据库知识、运维技巧。