使用MySQL 过程中,经常会碰到很多时区的问题,导致查询的时间跟我们预期的不符。这些问题很多时候是因为time_zone和timestamp导致的,总结了一篇内容分享给大家,希望能对大家有帮助。
timestamp和datetime
存储方式
timestamp
timestamp 会在当前时区的基础上,转换为 0时区(UTC)的时间戳存储到数据库中
datetime
datetime 是直接拿写入的日期直接存储到数据库中的
binlog实例
其中@7为datetime字段,@8为timestamp字段
BEGIN
/*!*/;
# at 40478
#210610 10:42:29 server id 3606 end_log_pos 40548 Table_map: `ops_hulk`.`user_test` mapped to number 57
# at 40548
#210610 10:42:29 server id 3606 end_log_pos 40625 Write_rows: table id 57 flags: STMT_END_F
BINLOG '
lezBYBMWDgAARgAAAGSeAAAAADkAAAAAAAEACG9wc19odWxrAAl1c2VyX3Rlc3QACQgIDw8PARIR
EQkAAQACgAAAAAAAAA==
lezBYBcWDgAATQAAALGeAAAAADkAAAAAAAEACf//AP4FAAAAAAAAAAQAAAAAAAAABQB0ZXN0NAUA
dGVzdDQAAJmp1Sj4YMHrDAAAAAA=
'/*!*/;
### INSERT INTO `ops_hulk`.`user_test`
### SET
### @1=5 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=4 /* LONGINT meta=0 nullable=0 is_null=0 */
### @3='test4' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */
### @4='test4' /* VARSTRING(512) meta=512 nullable=0 is_null=0 */
### @5='' /* VARSTRING(128) meta=128 nullable=0 is_null=0 */
### @6=0 /* TINYINT meta=0 nullable=0 is_null=0 */
### @7='2021-06-10 18:35:56' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
### @8=1623321356 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */
### @9=0 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */
# at 40625
#210610 10:42:29 server id 3606 end_log_pos 40652 Xid = 5498
COMMIT/*!*/;
数据展示
timestamp
timestamp 会获取当前的时区,然后将 0时区(UTC)的时间戳转换为当前时区下的日期
其中,这个时区转换函数需要调用系统函数
所以不同时区看到的日期是不一样的
datetime
datetime 会直接那记录的日期来展示,不涉及时区转换
所以不同时区看到的日期是不变的。
time_zone
time_zone 相关的参数有两个,分别是
- system_time_zone:当前服务器的时区
- time_zone:默认为 system,也就是用服务器的时区。如果有单独设置,那么会已这个时区为准。
官方解释
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.) By default, the current time zone for each connection is the server's time. The time zone can be set on a per-connection basis. As long as the time zone setting remains constant, you get back the same value you store. If you store a TIMESTAMP value, and then change the time zone and retrieve the value, the retrieved value is different from the value you stored. This occurs because the same time zone was not used for conversion in both directions. The current time zone is available as the value of the time_zone system variable. For more information, see Section 5.1.15, “MySQL Server Time Zone Support”.
time_zone对时间的影响
受影响的函数及类型
函数
now()
current_timestamp()
sysdate()
curdate()
unix_timestamp()
....
类型
timestamp
不受影响的函数及类型
函数
UTC_DATE()
UTC_TIMESTAMP()
...
类型
datetime
修改 time_zone 对timestamp及datetime的影响
读取
- datetime 不受 time_zone的影响,因为本身存储的就是日期,所以读出来的也是原日期;
- timestamp 读取是受time_zone影响的,因为存储的是时间戳,读取的时候,会根据time_zone 来换算出具体的时间。
写入
-
datetime 的写入是默认写入的时间是多少,那么数据库里存储的就是多少,比如'2021-06-11 05:03:06'。
- 但是,类似于我们的 current_timestamp ,因为这个是受 time_zone 影响的,所以,当时区变化,这类的datetime存储的值会跟着发生变化,也就是会发生 系统函数调用。
-
timestamp 的写入不受 time_zone的影响,因为写入的时间戳实际是已 0 时区来存储的
官方解释
If a column uses the DATETIME data type, then any inserted values are stored as-is, so no automatic time zone conversions are performed.
If a column uses the TIMESTAMP data type, then any inserted values are converted from the session's time zone to Coordinated Universal Time (UTC) when stored, and converted back to the session's time zone when retrieved.
time_zone=system与timestamp的性能问题
因为timestamp每次读取数据都需要通过convert_tz 函数,调用系统时区。
然后这个调用,会使得CPU产生自旋锁,导致CPU产生大量的上下文切换,负载被打满
官网解释
If set to SYSTEM, every MySQL function call that requires a time zone calculation makes a system library call to determine the current system time zone. This call may be protected by a global mutex, resulting in contention.
官网链接
time_zone 相关:
datetime 相关: