小豹子的网络记事本

记录每一个有意思的细节

Mysql - 数据库时区是客户端属性还是服务端属性

一、说明

同事问我数据库的时区是客户端属性还是服务端属性,我觉得这个问题十分有意思,之前没怎么留意,自己来做下实验。
首先介绍几个术语。
GMT(Greenwich Mean Time),格林尼治平均时间。
UTC(Coordinated Universal Time),协调世界时。
CST(China Standard Time),中国标准时间,也称北京时间。
根据维基百科,格林尼治标准时间的正午是指当平太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,目前已经被原子钟报时的协调世界时(UTC)所取代。协调世界时是世界上调节时钟和时间的主要时间标准,计算机中的网络时间协议(NTP, Network Time Protocol)就是用的协调世界时。不过,GMT和UTC相差不超过1秒,对绝大多数系统来说,可以认为这两个时间等价。中国的标准时间就是在UTC上面加8小时。

二、测试

测试使用Mysql8,CentOS7,Jdbc

我现在的时间是北京时间 2023-4-22 19:42,我将服务器的时间设置成任意一个时间,时区设置成东京,即(UTC+9)。

[root@xbz ~]# timedatectl list-timezones  // 列出所有时区
[root@xbz ~]# timedatectl set-timezone Asia/Tokyo  // 设置成东京时区
[root@xbz ~]# date -s '2023-4-20 15:00:00'  // 设置任意时间
[root@xbz ~]# date -R  // 查看系统时间
Thu, 20 Apr 2023 15:00:41 +0900 

将Mysql数据库重启,Mysql关于时区有两个参数
system_time_zone 系统时区,在Mysql启动时会检查当前系统的时区并根据系统时区设置。参数不可修改。
time_zone 会话时区,默认为system,即使用全局参数system_time_zone的值。可以动态修改。

(root@localhost)[(none)]> show variables like '%zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | JST    |
| time_zone        | SYSTEM |
+------------------+--------+

会话时区的修改可以使用命令set global time_zone='+8:00'修改,或者在参数文件中设置default_time_zone='+8:00'。为了实验,我将客户端的时区设置为'+11:00'。

(root@localhost)[(none)]> set global time_zone='+11:00';
(root@localhost)[(none)]> show variables like '%zone%';  // 再开一个窗口查询
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | JST    |
| time_zone        | +11:00 |
+------------------+--------+

数据库服务器在UTC+9,数据库客户端时区设置的是UTC+11。分别查服务器的时间和数据库的时间,数据库的时间相比服务器的时间要快了2小时,这就是参数time_zone在起作用。

[root@xbz ~]# date -R
Thu, 20 Apr 2023 15:10:58 +0900

(root@192.168.1.82)[(none)]> select sysdate();
+---------------------+
| sysdate()           |
+---------------------+
| 2023-04-20 17:10:45 |
+---------------------+

再来看一个例子,我在数据库中分别创建一个datetime和timestamp两个字段,插入同一个时间。然后将客户端的时区换一下,会发现timestamp的时间也跟着发生变化了,这其实是由于MySQL将timestamp列的值从当前时区转换为UTC时间进行存储,查询时,将数据从UTC转换为检索的当前时区。

(root@192.168.1.82)[hello]> create table t1(id int, dt datetime, tt timestamp);
(root@192.168.1.82)[hello]> insert into t1 values (1, '2022-01-01 12:00:00', '2022-01-01 12:00:00');
(root@192.168.1.82)[hello]> select * from t1;
+------+---------------------+---------------------+
| id   | dt                  | tt                  |
+------+---------------------+---------------------+
|    1 | 2022-01-01 12:00:00 | 2022-01-01 12:00:00 |
+------+---------------------+---------------------+

(root@192.168.1.82)[hello]> set time_zone='+12:00';
(root@192.168.1.82)[hello]> select * from t1;
+------+---------------------+---------------------+
| id   | dt                  | tt                  |
+------+---------------------+---------------------+
|    1 | 2022-01-01 12:00:00 | 2022-01-01 13:00:00 |
+------+---------------------+---------------------+

接下来测试下Jdbc中的表现

public class JdbcTimeZoneTest {
    public static void main(String[] args) throws Exception {
        String user = "scott";
        String password = "tiger";
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://192.168.1.82/hello";  
        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);
        String sql = "select sysdate()";
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()) {
            System.out.println(resultSet.getTimestamp(1));
        }
    }
}

运行结果:
2023-04-20 19:02:44.0

mysql客户端的结果:

(root@192.168.1.82)[(none)]> select sysdate();
+---------------------+
| sysdate()           |
+---------------------+
| 2023-04-20 19:02:47 |
+---------------------+

可以看到jdbc跟mysql客户端显示的时间一致。

url加入这个参数?serverTimezone=Asia/Shanghai
运行结果:
2023-04-20 19:03:19.0
可以看到时间好像没什么变化

当我把jdbc所运行的机器时间调整成(UTC+3)
微信截图_20230418170911.png

再运行一次,结果:
2023-04-20 14:04:17.0

mysql客户端的结果:

(root@192.168.1.82)[(none)]> select sysdate();
+---------------------+
| sysdate()           |
+---------------------+
| 2023-04-20 19:04:35 |
+---------------------+

jdbc所显示的时间为什么会比mysql客户端显示的时间慢5个小时呢,这是由于serverTimezone=Asia/Shanghai是在东八区,而我的本机设置的时区是东三区,差了5个小时,所以相较于Mysql客户端就有5个小时的差距。所以serverTimezone要么不设置,要么设置成jdbc所运行的机器的时区。

三、总结

  1. 设置好服务器的时区和mysql的时区,避免一些不必要的坑。
posted @ 2024-05-20 08:58  小豹子加油  阅读(4)  评论(0编辑  收藏  举报