最近有一个新项目上线,在上线时,突然发现时间与正常时间对不上,少了八个小时;但我丝毫不慌,这不就是个时区的问题吗,简单,但是这一次它给我深深的上了一课,一起来看整个排查过程吧。
开始排查
排查数据库
一般的时区问题都是数据库配置或数据链接参数的配置问题,于是我立马就定位到了问题,应该是数据库的时区设置错了,于是我愉快的查看了数据库时区
命令:show variables like '%time_zone%';
1、system_time_zone:全局参数,系统时区,在MySQL启动时会检查当前系统的时区并根据系统时区设置全局参数system_time_zone的值。值为CST,与系统时间的时区一致。
2、time_zone:全局参数,设置每个连接会话的时区,默认为SYSTEM,使用全局参数system_time_zone的值。
CST时间
CST时间:中央标准时间。CST可以代表如下4个不同的时区:
-
Central Standard Time (USA) UT-6:00,美国
-
Central Standard Time (Australia) UT+9:30,澳大利亚
-
China Standard Time UT+8:00,中国
-
Cuba Standard Time UT-4:00,古巴
再次分析
很显然,这里与UTC时间无关,它只是时间标准。目前Mysql中的system_time_zone是CST,而CST可以代表4个不同的时区,那么,Mysql把它当做哪个时区进行处理了呢?
简单推算一下,中国时间是UT+8:00,美国是 UT-6:00,当传入中国时间,直接转换为美国时间(未考虑时区问题),时间便慢了14个小时。
解决方案
方案一:修改数据库时区
既然是Mysql理解错了CST指定的时区,那么就将其设置为正确的。
连接Mysql数据库,设置正确的时区:
set global time_zone = '+8:00';
set time_zone = '+8:00';
flush privileges;
再次执行show命令查看:show variables like '%time\_zone%';
。
这里我选择方案2:修改数据库连接参数
## 原配置
serverTimezone=GMT%2B8
##修改 serverTimezone=Asia/Shanghai
url: jdbc:mysql://localhost:3306/aurora_admin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai&autoReconnect=true&rewriteBatchedStatements=true&allowMultiQueries=true
到这里,我想着问题肯定已经解决了;愉快的测了一手;结果还是差八个小时?但是本地项目的时间是正常的啊,我开始有了不祥的预感; 于是我本地连上线上的数据库开始测试,发现时间是正常的。到这里,就基本排查出不是MySQL的问题。
排查 Linux
我开始怀疑是不是 linux 系统的时区有问题;
查看硬件的时间:hwclock --show
查看系统的时间:date -R
发现Linux的时间是没问题的,于是开始查服务器的时区配置 查看时区 TZ配置:echo $TZ
发现为空,于是查看系统配置;查看系统配置命令:env
发现确实没有 TZ 的配置,系统并未设置TZ变量,而是通过localtime指定时区;于是我修改 localtime的指定。
先删除TZ环境变量:unset TZ
再执行:ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 命令etc目录下创建名称是localtime的快捷键指定到上海时区文件上;
修改完成后,果断重启项目测试,结果令人失望,时间依旧没有变化,于是尝试直接加上 TZ配置
命令:export TZ=Asia/Shanghai
再次查看:
可以看到 TZ配置已经设置成功,到这里我似乎看到了希望;于是再次测试,然后我人傻了,时间依旧少八个小时;到这里,我实在是不知道还有什么是没排查的了,于是脑海里重新过一遍排查的过程。
1、本地连线上的数据库测试是正常的,所以数据库肯定是没问题的
2、项目程序也没问题:@JsonFormat()并没有指定其他时区,字段类型也对的上;
3、Linux时间也没问题,时区也设置了;
排查Docker
我突然想到项目是通过Docker 来构建的,难道是Docker内部的时区有问题,这是唯一还没有排查的地方了,于是查看Dockerfile配置文件!
很简单的配置呀,没理由出问题呀,为了保险起见;决定手动给他指定一个时区,最终的配置文件;添加配置:
# 安装tzdata(如果 设置时区不生效使用)
# RUN apk add --no-cache tzdata
# 设置时区
ENV TZ="Asia/Shanghai"
重新构建项目运行,再次测试,时间正常了。
总结
整个排查过程虽然艰辛,但好在是解决了,我们在排查问题的时候,一定要胆大心细,多个地方考虑,很小伙伴可能想到是数据库的问题,但是发现修改配置后依然不行,可能会想是不是数据库版本问题呀,或者是不是我们项目哪儿写的有问题呀,把代码,配置看了一遍又一遍,虽然有这个可能,但是我们的思想就局限到这个地方了,就不敢去想,或者不愿去相信会是服务器问题,或其他的问题;我们应该培养这种发散的思想。