TimeZone-datetime在JVM时区和MySQL Session时区的转换

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
可观测可视化 Grafana 版,10个用户账号 1个月
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: TimeZone-datetime在JVM时区和MySQL Session时区的转换

引言

JVM时区配置-两行代码让我们一帮子人熬了一个通宵】描述了由于代码BUG导致存储到数据库的时间比正常时间少八小时的案例。案例中对于数据库字段类型是datetime和timestamp的时区转换关系进行了描述,本文试图从代码角度描述以下逻辑:

  • JDBC场景下MySQL Session时区如何配置的
  • JDBC场景下datetime类型的数据如何转换的

测试环境

MySQL

配置项 说明
MySQL version Windows MySQL Server 8.0.30.0
time_zone +08:00
system_time_zone
创建测试库 create database test;
创建测试表 create table datetimetest( dt datetime);

应用信息

java version

java version "1.8.0_341"
Java(TM) SE Runtime Environment (build 1.8.0_341-b10)
Java HotSpot(TM) Client VM (build 25.341-b10, mixed mode, sharing)

pom

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

分析过程

测试场景

  • JVM是UTC + 8,MySQL time_zone是UTC + 8,MySQL JDBC Driver配置的是UTC + 0
  • JVM 应用程序原始时间是(UTC + 8):2022-10-16 10:00:00
  • MySQL JDBC Driver发送给MySQL server的时间是:2022-10-16 02:00:00(时间由UTC + 8转换为UTC + 0)
  • MySQL server最终存储的时间为:2022-10-16 02:00:00
  • MySQL JDBC Driver从数据库中查出的时间是:2022-10-16 02:00:00
  • 应用程序最终读取到的时间是:2022-10-16 10:00:00

测试代码

getConnection

从图中看创建一个数据库连接是个非常重量级的操作,选择一个高效的连接池很重要。与本篇文章主要相关的是图中斜体红色加粗部分。

关注点一

关注跟time_zone相关的几个配置项。

相关类及配置说明文件:PropertyDefinitions、LocalizedErrorMessages.properties。

配置项 默认值 sinceVersion
connectionTimeZone 字符串类型,默认值:null 3.0.2
forceConnectionTimeZoneToSession 布尔类型,默认值:false 8.0.23
preserveInstants 布尔类型,默认值:true 8.0.23

关注点二

executeUpdate

PreparedStatement的实现类是:com.mysql.cj.jdbc.ClientPreparedStatement,跟本次文章相关的内容如下:

编码器

在NativeProtocol类初始化的时候会将不同数据类型的编码器注册&初始化:

static Map<Class<?>, Supplier<ValueEncoder>> DEFAULT_ENCODERS = new HashMap<>();
static {
  DEFAULT_ENCODERS.put(BigDecimal.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(BigInteger.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(Blob.class, BlobValueEncoder::new);
  DEFAULT_ENCODERS.put(Boolean.class, BooleanValueEncoder::new);
  DEFAULT_ENCODERS.put(Byte.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(byte[].class, ByteArrayValueEncoder::new);
  DEFAULT_ENCODERS.put(Calendar.class, UtilCalendarValueEncoder::new);
  DEFAULT_ENCODERS.put(Clob.class, ClobValueEncoder::new);
  DEFAULT_ENCODERS.put(Date.class, SqlDateValueEncoder::new);
  DEFAULT_ENCODERS.put(java.util.Date.class, UtilDateValueEncoder::new);
  DEFAULT_ENCODERS.put(Double.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(Duration.class, DurationValueEncoder::new);
  DEFAULT_ENCODERS.put(Float.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(InputStream.class, InputStreamValueEncoder::new);
  DEFAULT_ENCODERS.put(Instant.class, InstantValueEncoder::new);
  DEFAULT_ENCODERS.put(Integer.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(LocalDate.class, LocalDateValueEncoder::new);
  DEFAULT_ENCODERS.put(LocalDateTime.class, LocalDateTimeValueEncoder::new);
  DEFAULT_ENCODERS.put(LocalTime.class, LocalTimeValueEncoder::new);
  DEFAULT_ENCODERS.put(Long.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(OffsetDateTime.class, OffsetDateTimeValueEncoder::new);
  DEFAULT_ENCODERS.put(OffsetTime.class, OffsetTimeValueEncoder::new);
  DEFAULT_ENCODERS.put(Reader.class, ReaderValueEncoder::new);
  DEFAULT_ENCODERS.put(Short.class, NumberValueEncoder::new);
  DEFAULT_ENCODERS.put(String.class, StringValueEncoder::new);
  DEFAULT_ENCODERS.put(Time.class, SqlTimeValueEncoder::new);
  DEFAULT_ENCODERS.put(Timestamp.class, SqlTimestampValueEncoder::new);
  DEFAULT_ENCODERS.put(ZonedDateTime.class, ZonedDateTimeValueEncoder::new);
}

与datetime相关的是SqlTimestampValueEncoder

SqlTimestampValueEncoder

getTimestamp

ResultSet的实现类是:com.mysql.cj.jdbc.result.ResultSetImpl,getTimestamp主要涉及两部分:

  • MysqlTextValueDecoder将原始报文字段解析为InternalTimestamp对象
  • SqlTimestampValueFactory将InternalTimestamp对象解析为应用使用的Timestamp

MysqlTextValueDecoder

SqlTimestampValueFactory

总结

以上是对数据库字段类型为datetime在新增、查询时候的一些逻辑,记录下来以备忘;

另外数据库字段类型为timestamp的在存储的时候还会有一次转换,使用的时候需要注意。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6月前
|
网络协议 Java 关系型数据库
年薪50W阿里P7架构师必备知识:并发+JVM+多线程+Netty+MySQL
线程基础、线程之间的共享和协作一 线程基础、线程之间的共享和协作二 线程的并发工具类 线程的并发工具类、原子操作CAS 显式锁和AQS一 显式锁和AQS二 并发容器一 并发容器二 并发容器三、线程池一 线程池二、并发安全一
|
3月前
|
消息中间件 关系型数据库 MySQL
实时计算 Flink版产品使用问题之使用CTAS同步MySQL到Hologres时出现的时区差异,该如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
5月前
|
数据采集 DataWorks 监控
DataWorks产品使用合集之mysql-cdc读取数据写入到datahub中,datahub如何转换时区
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
60 1
|
5月前
|
关系型数据库 MySQL Go
Mysql查看数据库时区并设置时区
Mysql查看数据库时区并设置时区
424 0
|
6月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版操作报错之遇到MySQL服务器的时区偏移量(比UTC晚18000秒)与配置的亚洲/上海时区不匹配,如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
6月前
|
架构师 Java 关系型数据库
一线架构师开发总结:剖析并发编程+JVM性能,深入Tomcat与MySQL
每一个程序员都有自己清晰的职业规划和终极目标,无论之后是继续钻研技术,还是转管理岗、产品岗,都是需要自己具备有一定的实力,换句话说技术要牛逼。架构师,是很多程序员的终极目标,而成为一名Java架构师,那就需要对自己自身有一定要求,不仅技术能力要过硬,还需要有组织能力和提出解决方案的能力。那么作为架构师,需要掌握哪些技术呢?
一线架构师开发总结:剖析并发编程+JVM性能,深入Tomcat与MySQL
|
6月前
|
设计模式 Java 关系型数据库
BAT等大厂年薪30W+面试清单:JVM\MySQL\设计模式\分布式\微服务
疫情影响下招聘名额缩减不少,但阿里、腾讯、抖音、快手等互联网公司却加快了人才招聘的节奏。这里根据自身的实际经历,整理了一份面试这些大厂的清单,希望能帮助到大家查漏补缺,攻克面试难关。
|
6月前
|
关系型数据库 MySQL Java
MySQL与服务器时区问题实践总结
MySQL与服务器时区问题实践总结
948 0
|
JSON 关系型数据库 MySQL
Mysql时区差8个小时问题解决
Mysql时区差8个小时问题解决
308 1
|
存储 缓存 安全
TimeZone-改变JVM默认时区是否影响log4j打印日志中的日期时间?
TimeZone-改变JVM默认时区是否影响log4j打印日志中的日期时间?
276 1