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子查询的简单改写优化,文中的小修改主要将子查询改为关联从而降低查询时关联的次数,需要的朋友可以参考下
    2015-05-05
  • MySQL SELECT数据查看WHERE(AND OR IN NOT)语句

    MySQL SELECT数据查看WHERE(AND OR IN NOT)语句

    这篇文章主要介绍了MySQL SELECT数据查看WHERE(AND OR IN NOT)de 语句学习,非常适合新手小白朋友,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • mysql 索引的基础操作汇总(四)

    mysql 索引的基础操作汇总(四)

    这篇文章主要为大家详细介绍了mysql 索引的基础操作汇总,涵盖了创建和查看索引、删除索引等操作,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • MySQL中给自定义的字段查询结果添加排名的方法

    MySQL中给自定义的字段查询结果添加排名的方法

    这篇文章主要介绍了MySQL中给自定义的字段查询结果添加排名的方法,只需要对counter写一个小算式,非常简单,需要的朋友可以参考下
    2015-06-06
  • MySQL的Query Cache原理分析

    MySQL的Query Cache原理分析

    QueryCache(下面简称QC)是根据SQL语句来cache的。一个SQL查询如果以select开头,那么MySQL服务器将尝试对其使用QC。每个Cache都是以SQL文本作为key来存的。
    2008-07-07
  • Trae MySQL MCP 连接失败(Fail to start)的问题解决

    Trae MySQL MCP 连接失败(Fail to start)的

    本文详细介绍了使用Trae工具远程访问内网MySQL数据库时遇到的启动和认证兼容问题,并通过本地命令映射和环境变量注入的方式成功解决,具有一定的参考价值,感兴趣的可以了解一下
    2026-01-01
  • MySQL如何查看数据库连接数

    MySQL如何查看数据库连接数

    本文介绍了在MySQL中查看数据库连接数的多种方法,包括使用SHOWSTATUS命令、查询information_schema数据库、使用SHOWPROCESSLIST命令、查看最大连接数以及使用性能模式,每个方法都有详细的示例和注意事项,帮助你有效地监控和管理数据库连接
    2024-11-11
  • MySQL 截取字符串函数的sql语句

    MySQL 截取字符串函数的sql语句

    这篇文章主要介绍了MySQL 截取字符串函数的sql语句,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-04-04
  • mysql 8.0.22压缩包完整安装与配置教程图解(亲测安装有效)

    mysql 8.0.22压缩包完整安装与配置教程图解(亲测安装有效)

    这篇文章主要介绍了mysql 8.0.22压缩包完整安装与配置教程图解(亲测安装有效),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • MySQL中WITH AS语句的使用方法举例

    MySQL中WITH AS语句的使用方法举例

    在MySQL中WITH AS语法用于创建一个临时的命名查询(也称为子查询),这些子查询可以在后续的查询中引用,从而简化查询语句的编写,这篇文章主要给大家介绍了关于MySQL中WITH AS语句的使用方法,需要的朋友可以参考下
    2024-06-06

最新评论