zoukankan      html  css  js  c++  java
  • 【曹工杂谈】Mysql客户端上,时间为啥和本地差了整整13个小时,就离谱

    瞎扯一点非技术

    本来今天上午就打算写的,结果中途被别的事吸引了注意力,公司和某保险公司合作推了一个医疗保险,让我们给父母买,然后我研究了半天条款;又想起来之前买的支付宝那个好医保,也买了两年多了,但是条款也不怎么懂,查了下,感觉坑不少,都做好了理赔时撕逼的打算了。

    研究了公司的保险后,还是决定把支付宝那个玩意给退了。尤其是健康告知那一句:最近两年内有住院行为的,就算是不满足健康告知。

    我还打电话问了我爸妈,他们也不记得几年前到底住没住过院了,反正我个人感觉心里没底。下午找支付宝,客服都半天找不到。

    大家也可以多注意下。

    背景

    我负责的一个后台服务,负责接收客户端请求,同时写库。比如,创建一个任务,在代码里创建时间是直接new Date,然后写入数据库。然后,我用我的客户端软件去看那个创建时间的时候,是差了13个小时的。

    当然了,客户端软件看着差了13个小时,但是我web界面上查看,是没啥问题的。

    比如,我现在时间是21:02分,我在界面上创建了一个任务,然后我用的mysql客户端sqlyog去查看任务的创建时间:

    Ftask_id  Fcreate_time         
    --------  ---------------------
        4121  2021-06-19 08:02:36  
    

    咦,怎么是8点呢,和现在比,差了21 - 8 = 13个小时。

    这个时区问题,一般还是和mysql的一些variable相关的,比如,我们这么查了一下,

    SHOW VARIABLES LIKE '%zone%'
    

    结果如下:

    Variable_name     Value   
    ----------------  --------
    system_time_zone  CST     
    time_zone         SYSTEM  
    

    这个cst、system,虽然不懂,但感觉就是有点问题,这时候去某度某歌查一下,基本改改就解决了。

    但是,这个mysql实例上,不止我们一个数据库,上面有几十个库,我这也不敢直接改数据库配置,万一有人专门这么配置的呢?

    然后我问了下同事,他在这个实例上也有一个其他的数据库,但是比较奇怪的是,他在程序里new Date,写进来的时间,是对的。

    大家都是一个组的,都是同样的mybatis框架,不至于你可以,我不可以。

    我决定,找找原因。

    当然了,这么明显的bug,之前没发现?那倒不是,我web界面上查出来,是对的。

    虽然只是有点恶心人(mysql客户端看到的时间差了13小时,web前端没问题),但还是不能继续忍了。

    mysql server错 or sqlyog客户端错

    sqlyog在本机,mysql server在远端,我们可以wireshark抓包,看看mysql返回的,是不是对的

    wireshark上,选择正确的网卡,捕获表达式设为:tcp port 3306,然后开抓,然后跑去sqlyog上执行select语句。

    SELECT Ftask_id,Fcreate_time FROM t_task ORDER BY Fcreate_time DESC  LIMIT 1;
    

    然后,回到我们的wireshark,抓到了很多包:

    然后随便找一个右键-跟踪流-tcp流,就会把对应的这个tcp连接上的包全部以ascii显示出来,正常来说,一般mysql的报文,都是明文的,可以直接看到sql语句,和返回的结果啥的,但是,我本机这个sqlyog,不知道是不是版本很高的原因,少量语句可以明文显示,其他的就不是明文。

    不过吧,咱们暂时没时间和这个客户端耗着,我直接去应用所在的服务端上抓包吧,看看mysql server返回的,是什么样的。

    上图那个tcpdump语句,就是抓3306端口的包,不管3306是src端口,还是dst端口。然后相关的包,写入到3306.pcap里面,然后我们sz传到windows上,用wireshark来分析。

    大家注意看上图,mysql返回的就有问题,先把锅甩给mysql。

    但是,mysql只是个存储,既然存的数据有问题,那是不是说明,可能我们写的有问题呢?

    mysql server:谁写了个错误的时间给我,来领锅

    很尴尬啊,这个时间,是我们的服务端写进去的,这样的话,我们只能继续像上图那样抓包了:

    只是这次,我们要抓现行,抓写入的包,当然了,我这里为了讲解,已经提前抓了这个任务的。

    看吧,果然写入有问题,说明程序有问题,我们顺便看看mybatis logger记录的sql日志。

    但是,mybatis 日志里,记录的时间是对的,就是晚上9点。

    ok,我们理一下,我们程序里new date,mybatis写入,记录的日志是晚上9点,没问题;但是,最终发给mysql server的包,是晚13小时的。

    说明啥,可能mysql 建立的连接有点问题,我这时候去看了下本地代码的配置文件。

    commondb:
      database:
        url: jdbc:mysql://xxxxxx:3306/xxxx?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useTimezone=true&serverTimezone=GMT%2B8
    

    大家看这个配置,useTimezone=true&serverTimezone=GMT%2B8,一看就有点不对,什么时区,什么GMT。

    我本来以为就这个问题了,但是,我们这边,程序和配置是分开打包、分开部署的,大家可以理解为:配置中心。

    我去看了下线上配置,结果好像没问题,即:

    commondb:
      database:
        url: jdbc:mysql://xxxxxx:3306/xxxx?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
    

    本来就没有带后面那个时区的东西。

    我很怀疑,现在线上那个运行的java程序,到底commondb.database.url有没有问题,我想了好几个办法:

    1、 因为是spring boot的,所以一开始用http://xx.xx.xx.xx:8080/actuator/env之类的端口,去查看了一下,发现访问不通。后来发现,咱们的程序,没引入spring boot的actuator的jar包,作罢。

    2、本地使用jconsole、jvisualvm去连接这个运行着的java程序。

    ​ 这个怎么玩呢,首先,这个运行着的程序,需要是开了这几个jvm参数:

    -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
    

    然后,就可以去连接它了,用jconsole/jvisualvm都行。

    具体可以参考我以前的一个博文:https://www.cnblogs.com/grey-wolf/p/9235073.html

    当然,这个比较随机,有时可以连上,有时不行,我这次就不行,我还在本机wireshark抓了jconsole去连接这个远程java程序的包。

    抓包的过滤语句:tcp port 9999。

    抓到的包,是以tcp协议展示的,其实我们知道应用层的通信协议的话,可以手动右键--decode as--然后选择rmi,

    没错,java自带的那个rmi,就可以看到多一些信息。

    当然了,虽然多了些信息,我还是没明白为啥jconsole没连上。放弃。

    jconsole不行,最终我还是只能试试arthas了,阿里的那个,连上去那个java程序后,只能看看环境变量、System Property之类的,好像对我们要看的东西,于事无补。

    还记得吗,我们想看的是,commondb.database.url的值,思考了一会,最终只能暴力解决了,这个属性,好像被注入到一个bean里去了,他就是Datasource,但是想看到这个bean的值,没那么简单:

        @Bean(name = DATA_SOURCE)
        @ConfigurationProperties(prefix = DB_CONFIG_PREFIX)
        @Primary
        public DataSource omsDbDatabase() {
            DataSource build = DataSourceBuilder.create().build();
            return build;
        }
    

    所以,最终只能jmap,把堆内存dump下来了,然后使用eclipse memoryAnalyzer来分析。

    oql,大家不了解的,可以了解下,反正就是根据class来搜索内存中的对象。

    配置没问题,那,问题在哪里

    我这时候才想起来,既然服务器上这个java程序,配置没问题,也会出这个时区问题。那我本地,是不是也会有这个问题(按理说,我早该这么想,但是就是后知后觉),然后本地试了下,和服务器上表现一样,这时候,其实就可以慢慢debug了。

    但是,暂时也没深入去debug,我只是,排除了众多因素之后,我还是很奇怪,同事那个程序,为啥发送给mysql server的时间没问题,我这个就有问题,我于是,对比了一下双方的mysql-connector-java这个依赖,发现,咦,版本不一样啊。

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.16</version>
            </dependency>
    

    同事用的是5.1.41,我这边怎么这么高,8.0。于是,我试着改成了5.1.41,这把,果然发给mysql server的时间,就是对的了。

    网上的解释

    找到了大概的答案,我开始悠闲地去网上搜索答案,肯定有很多人升了版本后出这个问题啊,哈哈。我去看答案就行。

    比如这里就一个,mysql-connector-java升级到8.0后保存时间到数据库出现了时差

    https://blog.csdn.net/valsong/article/details/102582582

    具体的根本原因,我还没仔细看,为啥两个客户端版本有这个差异,不过,大概的排查过程,就是这样了。

    2021-07-19更新

    如果不修改pom.xml中的mysql版本的话,可以通过如下方式解决,也可以保证写到数据库的时间是正确的。

    commondb:
      database:
        url: jdbc:mysql://xxxxxx:3306/xxxxx?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
        username: root
        password: root1234
        maxActive: 10
        maxIdle: 5
        minIdle: 1
        initialSize: 1
        test-while-idle: true
        driver-class-name: com.mysql.jdbc.Driver
    
  • 相关阅读:
    SPOJ 694 (后缀数组) Distinct Substrings
    POJ 2774 (后缀数组 最长公共字串) Long Long Message
    POJ 3693 (后缀数组) Maximum repetition substring
    POJ 3261 (后缀数组 二分) Milk Patterns
    UVa 1149 (贪心) Bin Packing
    UVa 12206 (字符串哈希) Stammering Aliens
    UVa 11210 (DFS) Chinese Mahjong
    UVa (BFS) The Monocycle
    UVa 11624 (BFS) Fire!
    HDU 3032 (Nim博弈变形) Nim or not Nim?
  • 原文地址:https://www.cnblogs.com/grey-wolf/p/14911510.html
Copyright © 2011-2022 走看看