MySQL的 timze_zone 和 SpringBoot 的 serverTimezone 的设置

查看和修改 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 thesystem_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_54849873/article/details/124336431

当创建数据时:

当在创建数据的时候, 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_test1111111)
    这是因为上述数据库链接串中 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 | 39831715f0c0258897d5997cfcf94b67 | 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 | 5294688841174f05cfb2c355f6f91772 | StarDB_test11                 | robcheng |     1 | 2023-06-15 03:40:21 | 2023-06-15 03:40:21 |
	| 48 | 4655bdf331c58b7d1935d5aab7b8d9d7 | StarDB_test1111111            | 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 appVOList = appAssembler.entityToVoList(appList);

    以 app_code 为 StarDB_test1111111 的记录的创建时间: 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
MySQL的 timze_zone 和 SpringBoot 的 serverTimezone 的设置_第1张图片
Get 操作: serverTimezone=UTC
MySQL的 timze_zone 和 SpringBoot 的 serverTimezone 的设置_第2张图片
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_test1111111",
	            "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

	{
	    "result": [
	        ... ...
	        {
	            "uniqId": "4655bdf331c58b7d1935d5aab7b8d9d7",
	            "appCode": "StarDB_test1111111",
	            "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 ,   

你可能感兴趣的:(Spring,数据库,GO,mysql,数学建模,数据库)