查看和修改 MySQL 的时区
system_time_zone: 系统时区, 在MySQL启动时会检查当前系统的时区并根据系统时区设置全局参数system_time_zone的值。 system_time_zone 变量只有全局值没有会话值,不能动态修改,MySQL 启动时,将尝试自动确定服务器的时区,并使用它来设置 system_time_zone 系统变量, 此后该值不变。 The system time zone. When the server starts, it attempts to determine the time zone of the host machine automatically and uses it to set the system_time_zone system variable. The value does not change thereafter. time_zone: 用来设置每个连接会话的时区,默认为 system 时,使用全局参数 system_time_zone 的值。 The current time zone. This variable is used to initialize the time zone for each client that connects. By default, the initial value of this is 'SYSTEM' (which means, “use the value of system_time_zone”). --查看 mysql> show global variables like '%time%zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.00 sec) --修改全局时区,新创建的session都会被修改; 重启 mysql 服务就失效了 set global time_zone='+00:00'; --修改当前session的时区 set session time_zone='+00:00'; --方法2:在配置文件中添加,永久生效 [mysqld] default-time-zone='+08:00' 把时区信息导入到了mysql库,导入方法:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -S /tmp/mysqld.sock mysql 时区影响了什么,概括一下就两点: 1. NOW() 和 CURTIME() 系统函数的返回值受当前 session 的时区影响 不仅是select now(),包括insert .. values(now())、以及字段的 DEFAULT CURRENT_TIMESTAMP 属性也受此影响: mysql> set time_zone='+00:00'; Query OK, 0 rows affected (0.00 sec) mysql> mysql> select now(),CURTIME(); +---------------------+-----------+ | now() | CURTIME() | +---------------------+-----------+ | 2021-12-02 08:45:33 | 08:45:33 | +---------------------+-----------+ 1 row in set (0.00 sec) mysql> mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.00 sec) mysql> mysql> select now(),CURTIME(); +---------------------+-----------+ | now() | CURTIME() | +---------------------+-----------+ | 2021-12-02 16:45:39 | 16:45:39 | +---------------------+-----------+ 1 row in set (0.00 sec) 2. timestamp 数据类型字段存储的数据受时区影响 timestamp 数据类型会存储当时session的时区信息,读取时会根据当前 session 的时区进行转换; 而 datetime 数据类型插入的是什么值,再读取就是什么值,不受时区影响。 也可以理解为已经存储的数据是不会变的,只是 timestamp 类型数据在读取时会根据时区转换: mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.00 sec) mysql> mysql> create table t(ts timestamp, dt datetime); Query OK, 0 rows affected (0.02 sec) mysql> mysql> insert into t values('2021-12-02 16:45:39','2021-12-02 16:45:39'); Query OK, 1 row affected (0.00 sec) mysql> mysql> select * from t; +---------------------+---------------------+ | ts | dt | +---------------------+---------------------+ | 2021-12-02 16:45:39 | 2021-12-02 16:45:39 | +---------------------+---------------------+ 1 row in set (0.00 sec) mysql> mysql> set time_zone='+00:00'; Query OK, 0 rows affected (0.00 sec) mysql> mysql> select * from t; +---------------------+---------------------+ | ts | dt | +---------------------+---------------------+ | 2021-12-02 08:45:39 | 2021-12-02 16:45:39 | +---------------------+---------------------+ 1 row in set (0.00 sec) Q:已经运行一段时间的业务,修改MySQL的时区会影响已经存储的时间类型数据吗? A:完全不会,只会影响对 timestamp 数据类型的读取。这里不得不提一句,为啥要用 timestamp? 用 datetime 不香吗,范围更大,存储空间其实差别很小,赶紧加到开发规范中吧。
SpringBoot 应用:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver // 需要使用高版本的驱动,否则下面的结论可能对不上 url: jdbc:mysql://127.0.0.1:3306/flow_manager_dev?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true 或者 spring: datasource: driver-class-name: com.mysql.jdbc.Driver // 或者使用低版本的驱动,但是 url 变量中需要增加 useTimezone=true url: jdbc:mysql://127.0.0.1:3306/flow_manager_dev?characterEncoding=utf-8&useSSL=false&useTimezone=true&serverTimezone=Asia/Shanghai&allowMultiQueries=true useTimezone 参数来自搜索 mysql cst 时区: https://blog.csdn.net/m0_/article/details/
当创建数据时:
当在创建数据的时候, SpringBoot Java 代码可能是 app.setCreatedDate(new Date()); appRepository.save(app); serverTimezone=Asia/Shanghai 我们知道 new date() 返回的是带有应用服务器时区的时间比如 Thu Jun 15 14:03:47 CST 2023, 而实际当写入数据库的时间也是 2023-06-15 14:03:47,(对应的记录是 app_code 为 StarDB_test) 这是因为上述数据库链接串中 serverTimezone=Asia/Shanghai,正好和应用服务器的本地时区一样 serverTimezone=UTC 我们知道 new date() 返回的是带有应用服务器时区的时间比如 Thu Jun 15 14:11:18 CST 2023, 而实际当写入数据库的时间也是 2023-06-15 06:11:18,(对应的记录是 app_code 为 StarDB_test_ServerTimeZoneUTC) 这是因为上述数据库链接串中 serverTimezone=UTC,正好和应用服务器的本地时区差8个小时 数据库的时区信息和库表数据: mysql> show variables like "%time_zone%"; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | +04:00 | +------------------+--------+ 2 rows in set (0.00 sec) mysql> mysql> select id, uniq_id, app_code, creator, state, created_datef, modified_date from app; +----+----------------------------------+-------------------------------+----------+-------+---------------------+---------------------+ | id | uniq_id | app_code | creator | state | created_date | modified_date | +----+----------------------------------+-------------------------------+----------+-------+---------------------+---------------------+ | 1 | 14515ba1b56c785c98b36f6f6d509d85 | StarDB | robcheng | 1 | 2022-04-05 11:52:00 | 2022-06-06 17:46:52 | | 4 | ee4e0c109a88e98f25fd84e90ccca9c0 | DMS | robcheng | 1 | 2022-04-05 12:01:28 | 2022-05-05 19:43:45 | | 24 | f0c0d5997cfcf94b67 | StarDB_DEV | robcheng | 1 | 2022-05-18 14:33:40 | 2022-05-27 18:54:06 | | 46 | 788de6f75ea373b39651b69c796a8beb | StarDB_TEST | robcheng | 1 | 2022-05-27 18:56:05 | 2022-05-27 18:58:44 | | 47 | 74f05cfb2c355f6f91772 | StarDB_test11 | robcheng | 1 | 2023-06-15 03:40:21 | 2023-06-15 03:40:21 | | 48 | 4655bdf331c58b7d1935d5aab7b8d9d7 | StarDB_test | robcheng | 1 | 2023-06-15 14:03:47 | 2023-06-15 06:03:47 | | 49 | c8be0df667dac57006b78298fae609cf | StarDB_test_NoServerTimeZone | robcheng | 1 | 2023-06-15 06:08:12 | 2023-06-15 06:08:11 | | 50 | 8aad9fe2658f0e300b5d8db7faeb64e6 | StarDB_test_ServerTimeZoneUTC | robcheng | 1 | 2023-06-15 06:11:18 | 2023-06-15 06:11:18 | +----+----------------------------------+-------------------------------+----------+-------+---------------------+---------------------+ 8 rows in set (0.00 sec) mysql> mysql>
当获取数据列表时:
1. 当在获取列表时,实体专VO时,SpringBoot java 代码可能是 List<AppVO> appVOList = appAssembler.entityToVoList(appList); 以 app_code 为 StarDB_test 的记录的创建时间: 2023-06-15 14:03:47 为例: serverTimezone=Asia/Shanghai 程序会把 2023-06-15 14:03:47, 当作是东八区(serverTimezone时区)的 2023-06-15 14:03:47, 然后转换为应用服务区器所在的时区东八区的时间即 Thu Jun 15 14:03:47 CST 2023 (和断点调试截图对应) serverTimezone=UTC 程序会把 2023-06-15 14:03:47 当作是 UTC (serverTimezone时区)的 2023-06-15 14:03:47, 然后转换为应用服务区器所在的时区东八区的时间即 Thu Jun 15 22:03:47 CST 2023 (和断点调试截图对应)
Get 操作: serverTimezone=Asia/Shanghai
Get 操作: serverTimezone=UTC
2. 序列化 VO 返回给前端时:
API 返回前端的数据格式(即序列化时),会进一步经过 spring.jackson 配置的处理, 猜测其处理过程是:把 VO 里的带时区的时间,转换为 spring.jackson.time-zone spring.jackson.date-format 指定的时区和格式,返回前端。 从结果上来看,就是把数据库中的创建时间列带着 spring.datasource.url 中的 serverTimezone 的时区,转换为 spring.jackson.time-zone spring.jackson.date-format 的时区和格式返回给前端 spring.jackson.time-zone spring.jackson.date-format 的默认配置返回格式如下: { "result": [ ... ... { "uniqId": "4655bdf331c58b7d1935d5aab7b8d9d7", "appCode": "StarDB_test", "appDesc": "This is a distributed databae", "callbackUrl": "http://10.222.205.33:8030/o/executor/workflow", "creator": "robcheng", "createdDate": "2023-06-15T14:03:47.000+00:00", ---------默认是标准的 UTC 时间格式--------- "modifiedDate": "2023-06-15T06:03:47.000+00:00" }, ... ... ], "error": { "code": 0, "message": "", "status": "" }, "requestId": "1c9cf6f5-6d7c-4069-99ac-0fa2e65883af" } spring: jackson: date-format: yyyy-MM-dd HH:mm:ss // 如果需要修改为UTC 可设置为 yyyy-MM-dd'T'HH:mm:ss'Z' time-zone: GMT+8 property-naming-strategy: SNAKE_CASE // 控制的是返回的数据中key是驼峰还是蛇形即Jackson在序列化和反序列化时的规则。 { "result": [ ... ... { "uniqId": "4655bdf331c58b7d1935d5aab7b8d9d7", "appCode": "StarDB_test", "appDesc": "This is a distributed databae", "callbackUrl": "http://10.222.205.33:8030/o/executor/workflow", "creator": "robcheng", "createdDate": "2023-06-15 22:03:47", ---------spring.jackson.time-zone spring.jackson.date-format 的时区和格式--------- "modifiedDate": "2023-06-15 14:03:47" }, ... ... ], "error": { "code": 0, "message": "", "status": "" }, "requestId": "1c9cf6f5-6d7c-4069-99ac-0fa2e65883af" }
总结
serverTimeZone的作用就是指定web服务器和mysql服务器的会话期间的mysql服务器时区,就是临时指定mysql服务器的时区。
创建接口:从结果上来看,就是把应用服务器的 new Date() 转换成 spring.datasource.url 中的 serverTimezone 时区对应的 yyyy:mm:dd HH:MM:SS 格式的字符串保存到数据库里。
获取接口:从结果上来看,就是把数据库中的创建时间列带着 spring.datasource.url 中的 serverTimezone 时区,转换为 spring.jackson.time-zone spring.jackson.date-format 的时区和格式返回给前端
同理 golang 也有类似配置。
bean.DbEngine, err = utils.CreateOrmEngine(conf.DataBase.Account, conf.DataBase.Password, conf.DataBase.IP, conf.DataBase.Port, conf.DataBase.Schema, conf.DataBase.Charset, conf.DataBase.MaxIdle, conf.DataBase.MaxOpen) // The following is VERY import for debug the datetime of mysql // 在给创建时间赋值时,会把服务器所在时区的时间转换为 bean.DbEngine.DatabaseTZ 时区的时间保存; // 而在获取数据时会把数据库里的创建时间当成 bean.DbEngine.DatabaseTZ 时区的,然后转换为 bean.DbEngine.TZLocation 时区的时间 bean.DbEngine.DatabaseTZ = time.UTC ,
补充1: spring serverTimezone=UTC 和 CURRENT_TIMESTAMP 列
spring serverTimezone=UTC , mysql 的 global VARIABLES time_zone 是 CST, 那么对于类型为 datetime 默认值为 CURRENT_TIMESTAMP 表字段写入的时间是什么,请举例说明?
在这种配置下,字段类型为 datetime
,默认值为 CURRENT_TIMESTAMP
的情况下,写入的时间取决于 MySQL 服务器的时区设置。由于 MySQL 的 CURRENT_TIMESTAMP
是基于服务器的时区来生成时间戳的,如果服务器的时区(time_zone
全局变量)设置为 CST(假设这里指的是中国标准时间,即 UTC+8),那么不管你的 Spring 应用程序或 JDBC 连接如何配置时区,写入数据库的时间将是 CST 时间。
例子说明
假设当前 UTC 时间是 2024-06-19 12:00:00
(中午12点)。如果你的 MySQL 服务器时区设置为 CST(中国标准时间,UTC+8),那么:
当前 UTC 时间:`2024-06-19 12:00:00` 转换为 CST 时间:`2024-06-19 20:00:00`(因为 CST 是 UTC+8,所以比 UTC 快8小时)
当你在这个配置下向数据库插入一条记录,且该记录的某个 datetime
字段设置了默认值 CURRENT_TIMESTAMP
,那么该字段实际写入的时间将是 2024-06-19 20:00:00
,按照 CST 时区计算的时间。
注意事项
- 服务器时区影响:如上所述,MySQL 服务器的时区设置直接影响
CURRENT_TIMESTAMP
的结果,因为它基于服务器当前的时区。 - Spring/JDBC 时区设置:在 Spring 应用程序的 JDBC 连接字符串中设置
serverTimezone=UTC
主要影响应用程序如何解析从数据库读取的时间戳(即,它告诉 JDBC 驱动程序期望数据库时间戳是 UTC),以及如何将应用程序中的时间戳(假设它们是 UTC)发送给数据库。但这不会改变 MySQL 服务器内部生成CURRENT_TIMESTAMP
的行为。可以通过代码来验证上述结论:@Component public class TimeZoneChecker { @Autowired private SqlSession sqlSession; public String getCurrentTimeZone() { // 返回值是 global time_zone 的值(新建session时,session 的 time_zone 取自 global time_zone) // 确实和spring 的配置 `serverTimezone=UTC` 没关系 return sqlSession.selectOne("SELECT @@session.time_zone"); } }
解决策略
如果你希望无论 MySQL 服务器的时区设置如何,都保持使用 UTC 时间,你有以下几个选项:
- 更改 MySQL 服务器的时区:将 MySQL 服务器的全局时区设置为 UTC,这样
CURRENT_TIMESTAMP
就会生成 UTC 时间。 - 应用层面处理时间:在应用程序中显式处理时间戳,确保使用 UTC 时间,并在插入数据库之前手动设置这些值,而不是依赖于
CURRENT_TIMESTAMP
默认值。
补充2: mysql session time_zone 和 CURRENT_TIMESTAMP 列
mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.01 sec) mysql> mysql> show global variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.01 sec) mysql> mysql> use robertdb; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> mysql> show create table my_table \G * 1. row * Table: my_table Create Table: CREATE TABLE `my_table` ( `id` int(11) NOT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec) mysql> mysql> mysql> select * from my_table; Empty set (0.00 sec) mysql> mysql> INSERT INTO my_table (id) VALUES (1); Query OK, 1 row affected (0.00 sec) mysql> mysql> select * from my_table; +----+---------------------+ | id | created_at | +----+---------------------+ | 1 | 2024-06-19 13:28:49 | +----+---------------------+ 1 row in set (0.00 sec) mysql> mysql> mysql> mysql> set time_zone = '+04:00'; ---------修改 session 时区 Query OK, 0 rows affected (0.00 sec) mysql> mysql> mysql> INSERT INTO my_table (id) VALUES (2); Query OK, 1 row affected (0.00 sec) mysql> mysql> mysql> select * from my_table; +----+---------------------+ | id | created_at | +----+---------------------+ | 1 | 2024-06-19 13:28:49 | | 2 | 2024-06-19 09:30:02 | +----+---------------------+ 2 rows in set (0.00 sec) mysql>
补充3:spring.datasource.url 的serverTimezone=UTC会设置session的time_zone为UTC 吗?
在使用 spring.datasource.url
配置数据库连接时,包括 serverTimezone=UTC
参数确实会影响到 JDBC 连接的会话级别时区设置,但它的工作方式可能与直接设置 MySQL 会话 time_zone
变量有所不同。
当你在 JDBC 连接字符串中指定 serverTimezone=UTC
,你告诉 JDBC 驱动在与数据库服务器通信时应该使用 UTC 时区。这意味着:
- JDBC 驱动层面上的时区转换:JDBC 驱动会在发送时间戳到 MySQL 服务器以及从服务器接收时间戳时,使用指定的
serverTimezone
(在这个例子中是 UTC)进行转换。这样做的目的是确保无论 MySQL 服务器的时区设置如何,应用程序和数据库之间交换的时间戳都能正确地反映预期的时区。 - 对 MySQL 会话
time_zone
设置的影响:虽然serverTimezone
参数确实影响了 JDBC 驱动如何处理时间戳,但它并不直接等同于在 MySQL 会话级别使用SET time_zone = 'UTC';
。MySQL 服务器的全局时区设置和会话时区设置是由其自身的配置和 SQL 命令决定的。然而,在实践中,通过 JDBC 连接字符串设置serverTimezone
通常足以确保客户端和服务器之间的时间戳转换按预期工作,无需手动设置会话级别的time_zone
。
如果你需要在应用程序中显式地检查或设置 MySQL 会话的 time_zone
,你可以在应用程序启动时或在需要的地方执行 SQL 命令来实现,例如:
SET time_zone = '+00:00';
或者,如果你想要在 Spring Boot 应用程序中自动执行此类命令,可以使用连接初始化 SQL 脚本的功能,或者在应用程序启动时通过编程方式设置。
总之,serverTimezone=UTC
参数确实影响了通过该连接进行的时间戳处理,但它主要是通过 JDBC 驱动层面来实现的,而不是通过改变 MySQL 会话的 time_zone
变量。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/84430.html