【曹工雜談】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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM