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 时区配置如下,然后重写进行查询。

spring.jackson.time-zone=GMT+8
  • 连接 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时间类型序列化的变化差点让项目暴雷

你可能感兴趣的:(日常问题记录)