天天看點

Springboot-2.x更新後導緻時間少8小時

今天生産環境出現了一個 BUG ,清單中有一列是儲存時間,該字段的時間值比實際值少了8個小時。

問題分析

檢查程式配置與資料庫配置

首先聽說這個 BUG 的時候,認為是項目沒有配置資料庫連接配接的時區造成,或者是資料庫的時區配置不對。

檢查生産環境的項目配置後發現,資料庫的連接配接中配置有

serverTimezone=Asia/Shanghai

這一項,于是去檢查資料庫的時區配置。

show variables like'%time_zone'
    --------------------------
    system_time_zone	CST
    time_zone			SYSTEM
    --------------------------
    
    select now() 
    --------------------------
    2020-08-20 17:42:15.0
    --------------------------
           

最終發現資料庫的時區以及伺服器的時區時間都正常。

檢查入資料的程式

由于查詢的程式與入庫的程式并不是一個服務,而查詢的服務雖然配置了

serverTimezone=Asia/Shanghai

,但是入庫的程式并沒有配置,是以懷疑是否是因為入庫時間時沒配置時區導緻。

為了确認是否是因為這個原因,我寫了一個 demo 程式,分别測試了幾種情況,得到以下測試結果。

  • 連接配接 TiDB,未配置時區進行插入。

    目前時間:2020-08-20 18:13:22.0 資料庫入庫資料:2020-08-20 18:13:22.0

    未配置時區查詢:2020-08-20T10:13:22.000+00:00

    配置時區後查詢:2020-08-20T10:13:22.000+00:00

    該結果與 BUG 現象一緻。

  • 連接配接 TiDB,配置時區進行插入。

    目前時間:2020-08-20 18:19:01.0 資料庫入庫資料:2020-08-20 18:19:01.0

    未配置時區查詢:2020-08-20T10:19:01.000+00:00

    配置時區後查詢:2020-08-20T10:19:01.000+00:00

從上面的結果看,對于 TiDB 而言,無論入資料的程式配置不配置時區,資料庫中的資料均正常。無論配置不配置時區,查詢出來的結果都早8小時。

接下來對 MySQL 進行測試。

  • 連接配接 MySQL,未配置時區進行插入。

    目前時間:2020-08-20 18:29:18.0 資料庫入庫資料:2020-08-20 05:29:18.0

    未配置時區查詢:2020-08-20T10:29:18.000+00:00

    配置時區後查詢:2020-08-19T21:29:18.000+00:00

  • 連接配接 MySQL,配置時區進行插入。

    目前時間:2020-08-20 18:32:52.0 資料庫入庫資料:2020-08-20 18:32:52.0

    未配置時區查詢:2020-08-20T23:32:52.000+00:00

    配置時區後查詢:2020-08-20T10:32:52.000+00:00

從上面的結果看,對于 MySQL 而言,若不進行時區配置,将導緻入庫時間不對。另一方面,無論時區配置與否,都不能查詢到正确的時間,也就是說資料源的時區配置隻能保證入庫時間沒問題。

發現是 jackson 序列化對象時的問題

由于出現問題的接口都是通過 Controller 直接傳回實體對象,通過 @RestController 注解将對象解析為 json 資料傳回,是以,懷疑是 jackson 序列化時的時區導緻。對 demo 程式添加 jackson 時區配置如下,然後重寫進行查詢。

  • 連接配接 TiDB

    未配置時區查詢:2020-08-20T18:13:22.000+08:00

    配置時區後查詢:2020-08-20T18:13:22.000+08:00

  • 連接配接 MySQL

    未配置時區查詢:2020-08-21T07:32:52.000+08:00

    配置時區後查詢:2020-08-20T18:32:52.000+08:00

到此為止,确認是 jackson 在将對象轉為 json 時的問題。在加上上面的配置後問題得到修複,此外還可以通過代碼的形式自定義序列化方式。

不過這次的問題之前并沒有出現過,也就是說之前是好使的。

斷定是 springboot1.5 更新 2.x 後的問題

此次問題是在服務進行 springboot 版本更新之後才有的,在這之前的應用都是正常的。于是我嘗試更換 springboot 版本,上面的實驗的版本為 springboot-2.3.0,接下來更換為 springboot-1.5.9後進行測試。

  • 連接配接 TiDB

    未配置時區查詢:1597918402000,解析該時間戳得到2020-08-20 18:13:22

    配置時區後查詢:1597918402000,解析該時間戳得到2020-08-20 18:13:22

    未配置時區插入,目前時間:2020-08-21 10:12:34.0,資料庫資料:2020-08-21 10:12:34.0

    配置時區後插入,目前時間:2020-08-21 10:12:34.0,資料庫資料:2020-08-21 10:12:34.0

  • 連接配接 MySQL

    未配置時區查詢:1597919572000,解析該時間戳得到2020-08-20 18:32:52

    配置時區後查詢:1597919572000,解析該時間戳得到2020-08-20 18:32:52

    未配置時區插入,目前時間:2020-08-21 10:09:21.0,資料庫資料:2020-08-21 10:09:21.0

    配置時區插入,目前時間:2020-08-21 10:10:52.0,資料庫資料:2020-08-21 10:10:52.0

至此,可以斷定是由于 springboot-2.x 的 jackson 的序列化方式與 springboot-1.5 不同導緻。

另外,對于 TiDB 而言,時區配置無效。對于 MySQL 而言,springboot-2.x 以下版本時區配置無效;springboot-2.x 以上版本中必須配置時區,否則入庫資料異常。

在 springboot-2.x 以下版本中,Date 類型将序列化為時間戳;在 springboo-2.x 以上,Date 類型将序列化為 datetime 日期。在 springboot-2.x 以上版本必須配置 jackson 時區,否則 jackson 序列化後資料異常。

參考資料

[1] Spring Boot更新到2.x,Jackson對Date時間類型序列化的變化差點讓項目暴雷