文档章节

jdbc mysql connector 6 时区问题

purely
 purely
发布于 2017/07/21 17:20
字数 920
阅读 180
收藏 0

起因

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

------

© 著作权归作者所有

共有 人打赏支持
purely

purely

粉丝 18
博文 19
码字总数 8155
作品 0
杭州
高级程序员
私信 提问
5.04-springboot连接数据库启动报错

部分异常信息如下: 导致的原因: 由于mysql:mysql-connector-java 的版本问题导致的 mysql-connector-java 5 : 使用的是 com.mysql.jdbc.Driver mysql-connector-java 6及以上 : 使用的是...

静以修身2025
12/05
0
0
Tomcat生成的session持久化到MySQL

Telling Tomcat to save session records in MySQL 此部分内容摘自 MySQL cookbook 3th。具体内容不做翻译,哈哈,懒 The default Tomcat default session storage mechanism uses temporar......

xiaoheike
08/19
0
0
【Mysql】利用Mybatis3连接mysql获取datetime类型数据错误

mysql版本:myql-connector-java-8.0.11.jar mybatis3连接数据库代码如下: 执行结果: OPTDATATIME为数据库实际结果(OPTDATATIME是转为字符串取出的数据)。OPT_DATETIME0为Java自动转类型...

大白来袭
11/29
0
0
关于 spring ibatis多数据源问题

配置在这里就不多说了,基本在网上搜得一样, 项目启动后一旦访问数据库后,没几分钟就报异常,如下 Caused by: com.ibatis.common.jdbc.exception.NestedSQLException: --- The error occu...

樱花泪
2016/01/25
425
2
MySQL 8.0版本连接报错:Could not create connection to database server.

准备搭建一个Spring Boot 组合mybatis的项目,数据库采用的是MySQL 8.0.11按照以往的配置,使用插件mybatis-generator-maven-plugin生成代码时,一直报错Could not create connection to da...

幕三少
11/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【58沈剑 架构师之路】选redis还是memcache,源码怎么说

memcache和redis是互联网分层架构中,最常用的KV缓存。不少同学在选型的时候会纠结,到底是选择memcache还是redis。 画外音:不鼓励粗暴的实践,例如“memcache提供的功能是redis提供的功能的...

张锦飞
刚刚
0
0
不要依赖于线程调度器(72)

多个线程可运行时, 线程调度器决定哪些线程将会被运行、以及运行多长时间 任何操作系统在处理该问题时,会 尽力做到公正,但是策略却大相径庭 编写良好的程序不要依赖这种策略细节,否则程序...

Java搬砖工程师
4分钟前
0
0
路由器AP、路由、中继、桥接模式之间的区别

在TP-Link迷你无线路由器上一般有AP(接入点)模式、Router(无线路由)模式、Repeater(中继)模式、Bridge(桥接)模式、 Client(客户端)模式;已经属于模式很全的路由了,尽管仅仅只是一个小...

吴伟祥
4分钟前
0
0
初识kafka的zookeeper

最近项目中,使用redis进行消息的分发与订阅。这种模式就是一种多播的方式,但是随着消息的不断增加,消费端来不及处理所有的数据。在没有持久化的功能下,很多数据丢失了。当然,也可以使用...

孟飞阳
6分钟前
0
0
赋能时空云计算,阿里云数据库时空引擎Ganos上线

随着移动互联网、位置感知技术、对地观测技术的快速发展,时空信息已从传统GIS行业渗透到大众应用及各行各业。从静态POI(兴趣点)到APP位置信息,从导航电子地图到车辆行驶轨迹,从卫星影像...

阿里云官方博客
7分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部