Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL数据时区及datetime和timestamp存储的差异

MySQL数据时区问题以及datetime和timestamp类型存储的差异

作者:世樹

这篇文章主要介绍了MySQL数据时区问题以及datetime和timestamp类型存储的差异,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

MySQL数据时区问题及datetime和timestamp类型存储的差异

问题:

查询不同数据库上表中记录时间差距8小时。

昨天协助其他地区同事解决客户查询到不同数据中心时间差距8小时的问题。原因就是时区不同。

解决方案:

设置服务器的时区都为北京时间,即修改数据库服务器的time_zone值为“+8:00”解决。

这个参数,可以在通过mysqld命令启动数据库的时候加上参数 --default-time-zone=timezone来设置时区,

也可以通过my.cnf配置文件的[mysqld]标签里增加 default-time-zone='timezone'这一行来设置。

如果希望即时生效,也可以通过命令修改全局或者会话级别的time_zone的值:

命令如下:

修改全局time_zone的值
set global time_zone='+8:00';
或
修改当前会话的time_zone
set time_zone='+8:00';

其他:

因为使用的云数据库也有在海外地区的,所以针对我们有些表的字段是timestamp类型的情况和遇到的问题,也一起整理了一下。

测试环境为 Windows MySQL 5.7.22-log。

表结构如下:

CREATE TABLE `ee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date1` datetime(6) DEFAULT NULL,
  `date2` time(2) DEFAULT NULL,
  `date3` timestamp(3) NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

当前数据库服务器的时区设置为(这是本次测试起始的系统时区情况):

即:system_time_zone参数值为+2:00。

test1

测试目的:同一个会话的time_zone值变化时,插入记录的时间显示的“不同

当前数据库time_zone参数情况如下;

root@iris>show global variables like'%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone |        |
| time_zone        | SYSTEM |# 数据库使用时区跟system一致,即当前系统时区,是耶路撒冷(+2:00),等同于set time_zone='+2:00'了。
+------------------+--------+

向表iris.ee中插入数据:

INSERT INTO iris.ee(date1,date2,date3) VALUES(NOW(),NOW(),NOW());

查询表数据:

root@iris>select * from iris.ee;
+----+----------------------------+-------------+-------------------------+
| id | date1                      | date2       | date3                   |
+----+----------------------------+-------------+-------------------------+
|  1 | 2018-12-07 09:55:52.000000 | 09:55:52.00 | 2018-12-07 09:55:52.000 |
+----+----------------------------+-------------+-------------------------+
1 row in set (0.00 sec)

修改当前会话的时区:

root@iris>set time_zone='+8:00';
Query OK, 0 rows affected (0.00 sec)

再次插入数据,并查看表数据:

root@iris>INSERT INTO iris.ee(date1,date2,date3) VALUES(NOW(),NOW(),NOW());
Query OK, 1 row affected (0.18 sec)
 
#查看表数据
root@iris>select * from iris.ee;
+----+----------------------------+-------------+-------------------------+
| id | date1                      | date2       | date3                   |
+----+----------------------------+-------------+-------------------------+
|  1 | 2018-12-07 09:55:52.000000 | 09:55:52.00 | 2018-12-07 17:55:52.000 |
|  2 | 2018-12-07 17:57:29.000000 | 17:57:29.00 | 2018-12-07 17:57:29.000 | # 看date1的值:原来时区是耶路撒冷(+2:00),改为“+8:00”后,时间变晚了6个小时,从九点变成17点。(分秒的差距是由于执行间隔时间造成)
+----+----------------------------+-------------+-------------------------+
2 rows in set (0.00 sec)

注意:这里修改time_zone都是会话变量,同一个会话里进行的。

test2

测试目的:修改会话级别和全局级别变量time_zone的差异;

当前表记录为:

root@iris>select * from iris.ee;
+----+----------------------------+-------------+-------------------------+
| id | date1                      | date2       | date3                   |
+----+----------------------------+-------------+-------------------------+
|  1 | 2018-12-07 09:55:52.000000 | 09:55:52.00 | 2018-12-07 22:55:52.000 |
|  2 | 2018-12-07 17:57:29.000000 | 17:57:29.00 | 2018-12-07 22:57:29.000 |
|  3 | 2018-12-07 23:52:08.000000 | 23:52:08.00 | 2018-12-07 23:52:08.000 |
|  4 | 2018-12-07 18:52:31.000000 | 18:52:31.00 | 2018-12-07 23:52:31.000 |
|  5 | 2018-12-07 19:04:08.000000 | 19:04:08.00 | 2018-12-08 00:04:08.000 |
|  6 | 2018-12-07 19:04:13.000000 | 19:04:13.00 | 2018-12-08 00:04:13.000 |
|  7 | 2018-12-07 19:04:34.000000 | 19:04:34.00 | 2018-12-08 00:04:34.000 |
+----+----------------------------+-------------+-------------------------+
7 rows in set (0.00 sec)

当使用全局级别变量时:

会话1:

会话2:

❤上面两图显示两个会话当前global time_zone都是“+8:00”。

分别在会话1和会话2中插入记录:

session1:
INSERT INTO iris.ee(id,date1,date2,date3) VALUES(11,NOW(),NOW(),NOW());
 
session2:
INSERT INTO iris.ee(id,date1,date2,date3) VALUES(22,NOW(),NOW(),NOW());

查询表记录:

会话1:

会话2:

❤上面两图表示当前插入的记录在两个会话中时间显示是(相对)一样的(都是同一个时区、当前实际时间--即,我看到手表上显示的时间。)

当使用会话级参数设置时:

会话1:

会话2:

❤上面两图表示,当会话1时区为“+1:00”、会话2时区为“+11:00”,相差10小时。

注意

(1)插入的记录:

datetime类型字段,保存的时间都是当前会话所设置的时区相应时间点(即,好比我两个会话都是修改了计算机的系统时间的时区,然后执行insert。执行命令时看到计算机上显示的时间)。

如下图;

而,timestamp类型字段,id=33 和id=44 的记录,是一样的(即,虽然我设定两个会话所在时区不同,但是我同时(假使是同时执行实际,我切换会话执行,时间也就隔了12秒,看上图)在两个会话里insert了,此时他们换算成我所在的时区的时间都是一样的,都跟我现在看到我手表的时间是一样的。),这也是我要说的第二个注意点,timestamp的时区性

(2)时间类型不同导致数据显示结果的不同:

这里涉及到一个问题,timestamp字段类型有时区性,上一个注意事项提到的。

datetime类型的字段的记录,记录的都是数据插入的时候当前会话所获取到的time_zone相应的时区的即时时间。就算会话或者说服务器设置的时区改变了了,表里这个字段的值也不会发生变化。

但是timestamp类型的字段的值会随着服务器时区的变化,自动换算成相应的时间。即在不同时区,查询到同一个条记录此字段的值会不一样。

可能说的比较绕。 还是不明白,可以再来看第二个例子。

例1:

表的数据没做任何update,在同一个会话里,修改了两次time_zone,看到的表ee里字段date3 (timestamp类型)的值变化了,date1(datetime类型)没变。

root@iris>set time_zone='+0:00';
Query OK, 0 rows affected (0.00 sec)
 
root@iris>select * from iris.ee;
+----+----------------------------+-------------+-------------------------+
| id | date1                      | date2       | date3                   |
+----+----------------------------+-------------+-------------------------+
|  1 | 2018-12-07 09:55:52.000000 | 09:55:52.00 | 2018-12-07 09:55:52.000 |
|  2 | 2018-12-07 17:57:29.000000 | 17:57:29.00 | 2018-12-07 09:57:29.000 |
+----+----------------------------+-------------+-------------------------+
2 rows in set (0.00 sec)
root@iris>set time_zone='+13:00';
Query OK, 0 rows affected (0.00 sec)
 
root@iris>select * from iris.ee;
+----+----------------------------+-------------+-------------------------+
| id | date1                      | date2       | date3                   |
+----+----------------------------+-------------+-------------------------+
|  1 | 2018-12-07 09:55:52.000000 | 09:55:52.00 | 2018-12-07 22:55:52.000 |
|  2 | 2018-12-07 17:57:29.000000 | 17:57:29.00 | 2018-12-07 22:57:29.000 |
+----+----------------------------+-------------+-------------------------+
2 rows in set (0.00 sec)
root@iris>

总结

上面两次对本会话的time_zone进行修改,得到date1字段的值没变,date3字段的值变了。

所以,表中有需要使用时间类型的字段时候,需要根据业务情况选择合适类型,datetime或者timestamp。

当然,这两个类型的差别并不仅限于其时区性,

datetime取值范围:0000-00-00 00:00:00 ~ 9999-12-31 23:59:59;

timestamp取值范围:1970-01-01 08:00:01!2038-01-19 11:14:07 。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文