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

 更新时间:2023年11月07日 09:01:06   作者:世樹  
这篇文章主要介绍了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类型)没变。

  • time_zone='+0:00'
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)
  • time_zone='+13:00' 
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 。

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

相关文章

  • MYSQL配置参数优化详解

    MYSQL配置参数优化详解

    MySQL是优化难度最大的一个部分,不但需要理解一些MySQL专业知识,同时还需要长时间的观察统计并且根据经验 进行判断,然后设置合理的参数。下面我们了解一下MySQL优化的一些基础
    2018-07-07
  • MySQL 中行转列的方法

    MySQL 中行转列的方法

    这篇文章主要介绍了MySQL 中行转列的方法,帮助大家更好的理解和学习MySQL的使用,感兴趣的朋友可以了解下
    2020-12-12
  • MYSQL初学者命令行使用指南

    MYSQL初学者命令行使用指南

    其实MYSQL的对数据库的操作与其它的SQL类数据库大同小异,您最好找本将SQL的书看看。我在这里只介绍一些基本的,其实我也就只懂这些了,呵呵。最好的MYSQL教程还是“晏子“译的“MYSQL中文参考手册“不仅免费每个相关网站都有下载,而且它是最权威的。
    2008-06-06
  • Mysql主从复制作用和工作原理详解

    Mysql主从复制作用和工作原理详解

    这篇文章主要介绍了Mysql主从复制作用和工作原理详解,主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库,主数据库一般是准实时的业务数据库,需要的朋友可以参考下
    2019-07-07
  • MySQL5.7主从复制详细配置教程

    MySQL5.7主从复制详细配置教程

    这篇文章主要介绍了MySQL5.7主从复制详细配置教程的相关资料,需要的朋友可以参考下
    2022-11-11
  • MySql 知识点之事务、索引、锁原理与用法解析

    MySql 知识点之事务、索引、锁原理与用法解析

    这篇文章主要介绍了MySql 知识点之事务、索引、锁原理与用法,结合实例形式较为详细的分析了mysql数据库事务、索引、锁的概念、原理、使用方法及相关操作注意事项,需要的朋友可以参考下
    2019-09-09
  • MySQL的分区表使用场景及示例小结

    MySQL的分区表使用场景及示例小结

    MySQL的分区表功能在某些场景下可以显著提高查询效率,本文主要介绍了MySQL的分区表使用场景及示例小结,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • Mysql表的操作方法详细介绍

    Mysql表的操作方法详细介绍

    这篇文章主要为大家详细介绍了MySQL数据库中表常用的一些操作方法,文中的示例代码讲解详细, 对我们学习MySQL有一定帮助,需要的可以参考一下
    2022-09-09
  • MySQL 8.0 新特性之检查约束的实现

    MySQL 8.0 新特性之检查约束的实现

    这篇文章主要介绍了MySQL 8.0 新特性之检查约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • mysql 8.0.12 快速安装教程

    mysql 8.0.12 快速安装教程

    这篇文章主要为大家详细介绍了mysql 8.0.12的快速安装教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08

最新评论