jdbc mysql connector 6 时区问题

起因

之前因为mysql connector 5的某个版本有OOM的风险: 传送门

所以调整了timeout时间和升级了connector到6.

一切以为都是正常的,不过却出现了一场惊天动地的抢险修复?。

苦难

事情出在第二天,刚好周六。运营反馈说线上所有的贷款借据日期都不准了,差了14个小时。

于事在家里干紧vpn连上后,查看情况。

不看不知道,一看吓一跳,还真的TM都差了14个小时。这不是要人命吗,别说数据对不上,关这一天的利息就不知道多少钱了,想想就可怕啊。

分析

此时最需要的时冷静。待人静下心来,以及和同事沟通排查。这个程序上线半年多没出现这个问题,那么肯定是最近的更新导致的。最近的更新就是改了timeout和升级了 jdbc mysql connector的版本。timeout应该不会有问题,那问题很可能在connector上。

于是在本地新建一个工程用新版connector连上mysql试了一下,果然差了14个小时。

解决

问题找到了,那就要解决啊。一开始还真不知道怎么解决,问题找到了,然没有查到根本原因啊。后面查了查万能的google,发现在jdbc的url里指定timezone为东8区就可以了,如下

serverTimezone=GMT%2b8:00

这里的%2b是url encode后的+,decode后就是serverTimezone=GMT+8:00

原理

既然知道怎么解决了,在解决完问题,修复数据后,必须要查清楚具体原因。后来仔细排查下来,要jdk的文档中找到了答案。

先来看一下mysql的时区设置:

show variables like '%time_zone%';
system_time_zone	CST
time_zone	SYSTEM

可以看出system_time_zone是CST,也就是东8区,time_zone用的是SYSTEM,那也就是system_time_zone的CST。

然后再看一下connector里怎么处理时区的,在ConnectionImpl类中,有一个

 private void initializePropsFromServer() throws SQLException

在里面可以看到初时化时区的方法

this.session.configureTimezone();

点进去查看

   public void configureTimezone() {
        String configuredTimeZoneOnServer = getServerVariable("time_zone");

        if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
            configuredTimeZoneOnServer = getServerVariable("system_time_zone");
        }

        String canonicalTimezone = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_serverTimezone).getValue();

        if (configuredTimeZoneOnServer != null) {
            // user can override this with driver properties, so don't detect if that's the case
            if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) {
                try {
                    canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());
                } catch (IllegalArgumentException iae) {
                    throw ExceptionFactory.createException(WrongArgumentException.class, iae.getMessage(), getExceptionInterceptor());
                }
            }
        }

        if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
            this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone);

            //
            // The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this...
            //
            if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverTimezoneTZ.getID().equals("GMT")) {
                throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.9", new Object[] { canonicalTimezone }),
                        getExceptionInterceptor());
            }
        }

        this.defaultTimeZone = this.serverTimezoneTZ;
    }

这里的PropertyDefinitions.PNAME_serverTimezone就是jdbc url上的serverTimezone变量,

可以很清楚地看出优先使用serverTimezon变量,如果没有,则用mysql的time_zone。

在没有serverTimezone变量时,读取mysql的time_zone为SYSTEM, 则进一步读取system_time_zone,也就是CST。

然后用CST做为时区,那问题来了,CST不就是东8区吗,怎么会有问题?

查看jdk的TimeZone类,看到注释中有这个一句:

* For compatibility with JDK 1.1.x, some other three-letter time zone IDs
* (such as "PST", "CTT", "AST") are also supported. However, <strong>their
* use is deprecated</strong> because the same abbreviation is often used
* for multiple time zones (for example, "CST" could be U.S. "Central Standard
* Time" and "China Standard Time"), and the Java platform can then only
* recognize one of them.

看到坑了没?CST可以是美国的中央时区,也可以是中国标准时区。所以现在问题已经找到了。

  1. 在mysql中CST代表的是中国标准时区,东8区。
  2. 在jdk中,CST代表的是美国中央时区

所以这里就出现在时区错乱的现象。

在jdk中,CTT表示东8区,所以也可以用serverTimezone=CTT,不过最好用GMT+8:00

ps:jdk版本1.8

------

转载于:https://my.oschina.net/purely/blog/1477281

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值