jdbc mysql connector 6 时区问题
博客专区 > purely 的博客 > 博客详情
jdbc mysql connector 6 时区问题
purely 发表于6个月前
jdbc mysql connector 6 时区问题
  • 发表于 6个月前
  • 阅读 45
  • 收藏 0
  • 点赞 0
  • 评论 0

标题:腾讯云 新注册用户域名抢购1元起>>>   

起因

之前因为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

------

标签: mysql jdk jdbc 时区
共有 人打赏支持
purely
粉丝 18
博文 18
码字总数 8155
×
purely
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: