作者:小傅哥 博客:https://bugstack.cn
❝沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞
大家好,我是技术UP主小傅哥。
在实际的工作场景中有时候就是一个小小的问题,就可能引发出一个大大的bug。而且工作这么多年,看到的线上事故,往往也都是这些小的细节问题,所以学习这些具有实际经验的细节非常重要。
有些事故隐藏的很深!
其实很多时候事故也不是一开始就有的,而是随着需求的迭代,达到某一个条件后触达到事故的发生条件了才出现的。就像 MySQL 的时区配置问题,它既有不同版本 JDBC 连接引擎的不同,又有数据库设置的时区,还有服务端设置的时区,还包括在使用数据库配置时指定的时区。这些条件综合发生时才会出现事故。
接下来,小傅哥就给大家分享下为啥是 8.0.22 版本才会引发时区错误问题。
这是一条很普通的SQL语句;
<insert id="insert" parameterType="cn.bugstack.xfg.dev.tech.infrastructure.po.EmployeePO">
INSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time)
VALUES(#{employeeNumber}, #{employeeName}, #{employeeLevel}, #{employeeTitle}, now(), now())
</insert>
修改下这条普通的SQL语句;
<insert id="insert" parameterType="cn.bugstack.xfg.dev.tech.infrastructure.po.EmployeePO">
INSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time)
VALUES(#{employeeNumber}, #{employeeName}, #{employeeLevel}, #{employeeTitle}, #{createTime}, now())
</insert>
接下来在执行插入SQL语句;
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-23.html
jdbc:mysql://127.0.0.1:3306/road-map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true
&serverTimezone=Asia/Shanghai
show variables like '%time_zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | CST |
| time_zone | SYSTEM |
+------------------+--------+
SET time_zone = 'SYSTEM';
SET time_zone = '+8:00';
[root@lavm-aqhgp9nber ~]# timedatectl
Local time: Sat 2024-08-31 13:57:07 CST
Universal time: Sat 2024-08-31 05:57:07 UTC
RTC time: Sat 2024-08-31 05:57:06
Time zone: Asia/Shanghai (CST, +0800)
NTP enabled: yes
NTP synchronized: yes
RTC in local TZ: no
DST active: n/a
sudo timedatectl set-timezone Asia/Shanghai
sudo timedatectl set-timezone America/New_York
在 8.0.0 ~ 8.0.22 版本中,如果未配置时区,serverTimezone=Asia/Shanghai
则会取服务端时区,所以如果服务端配置的是 CST 时区,则会有问题。调试源码;
com.mysql.cj.protocol.a.NativeProtocol#configureTimezone
jdbc:mysql://IP:13306/road-map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
在 8.0.23 版本以后,如果未配置时区,调整为获取客户端时区。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
com.mysql.cj.protocol.a.NativeProtocol#configureTimezone
Gets the default TimeZone of the Java virtual machine.
获取 Java 虚拟机默认时区。这个方法也是 Java 本身代码的方法。System.*out*.println("Default Time Zone: " + TimeZone.getDefault().getID());
获取默认时区。打印结果为 Default Time Zone: Asia/Shanghai
地址:https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-23.html
Bugs Fixed
After upgrading from Connector/J 5.1 to 8.0, the results of saving and then retrieving DATETIME and TIMESTAMP values became different sometimes. It was because while Connector/J 5.1 does not preserve a time instant by default, Connector/J 8.0.22 and earlier tried to do so by converting a timestamp to the server's session time zone before sending its value to the server. In this release, new mechanisms for controlling timezone conversion has been introduced—see Preserving Time Instants for details. Under this new mechanism, the default behavior of Connector/J 5.1 in this respect is preserved by setting the connection property preserveInstants=false. (Bug #30962953, Bug #98695, Bug #30573281, Bug #95644)
从 Connector/J 5.1 升级到 8.0 后,保存和检索 DATETIME 和 TIMESTAMP 值的结果有时会有所不同。这是因为,虽然 Connector/J 5.1 默认不保留时间点,但 Connector/J 8.0.22 及更早版本尝试通过在将时间戳的值发送到服务器之前将其转换为服务器的会话时区来保留时间点。在此版本中,引入了用于控制时区转换的新机制 - 有关详细信息,请参阅保留时间点。在这种新机制下,通过设置连接属性 retainInstants=false 来保留 Connector/J 5.1 在这方面的默认行为。(错误 #30962953、错误 #98695、错误 #30573281、错误 #95644)
在使用MySQL的时候,确保服务器时区、MySQL时区、Java应用链接MySQL JDBC的参数配置,都指定到具体的时区上。MySQL JDBC 使用 8.0.23+ 版本,不要使用 8.0.0 ~ 8.0.22 版本,尤其是5.1升级要升级到 8.0.23 以及往后的版本。
正确配置;url: jdbc:mysql://127.0.0.1:3306/road-map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
小傅哥的星球「码农会锁」有非常多的实战项目,包括业务的5个;大营销(大课)
、OpenAI 大模型应用
、Lottery
、IM
、AI 问答助手
。组件的7个;OpenAI 代码评审
、BCP 透视业务监控
、动态线程池
、支付SDK设计和开发
、API网关
、SpringBoot Starter
、IDEA Plugin 插件开发
。