直接设为 '+08:00' 最稳妥,因 system 依赖系统时区易导致 8 小时偏差;需在配置文件中设置 default-time-zone = '+08:00' 并重启服务,同时客户端连接须显式指定时区,且 timestamp 与 datetime 时区行为不同,须四者对齐。

MySQL 服务端时区设为 +08:00 还是 SYSTEM?
直接设成 +08:00 最稳妥,尤其当系统时区(SYSTEM)不是东八区时——比如 Docker 容器默认用 UTC,或 Linux 主机时区被误改,此时 SYSTEM 会悄无声息引入 8 小时偏差。
实操建议:
- 优先在 MySQL 配置文件(如
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf)的[mysqld]段落加:default-time-zone = '+08:00'
- 避免写
default-time-zone = 'Asia/Shanghai':该值依赖系统 tzdata,部分精简镜像(如 Alpine)可能缺失,导致启动失败 - 修改后必须重启 MySQL(
systemctl restart mysql),仅执行SET GLOBAL time_zone = '+08:00'是临时的,服务重启即失效
连接时显式指定时区比依赖服务端更可靠
即使服务端设对了,客户端连接若未声明时区,仍可能因驱动默认行为出问题。例如 Java 的 JDBC 默认用 JVM 本地时区解析 TIMESTAMP,而 Python 的 pymysql 默认用 UTC。
关键操作:
- JDBC 连接串务必加上:
?serverTimezone=GMT%2B8&useTimezone=true(注意 URL 编码) - Node.js 的
mysql2库需在配置中设:timezone: '+08:00' - 命令行客户端(
mysql)连接时加--default-authentication-plugin=mysql_native_password不解决时区问题,得靠SET time_zone = '+08:00'手动切(但只对当前会话有效)
TIMESTAMP 和 DATETIME 对时区的反应完全不同
这是最常踩的坑:TIMESTAMP 值存入和读取时会按当前会话时区自动转换,DATETIME 则原样存储、不转换——哪怕你把服务端时区改成 +08:00,已存的 DATETIME 字段也不会“自动修正”。
排查与处理:
- 查当前会话时区:
SELECT @@session.time_zone; - 查字段类型:
SHOW COLUMNS FROM table_name LIKE 'created_at';确认是TIMESTAMP还是DATETIME - 如果历史数据已错存 8 小时(比如用
DATETIME存了 UTC 时间却当北京时间用),不能靠改时区挽回,得用DATE_ADD()批量修正:UPDATE table_name SET created_at = DATE_ADD(created_at, INTERVAL 8 HOUR) WHERE ...
验证是否真生效:别只看 NOW()
NOW() 返回的是会话时区下的时间,容易误判。真正要验的是「存-取一致性」:
执行这两步:
- 插入:
INSERT INTO test_table (ts_col, dt_col) VALUES (NOW(), NOW()); - 立刻查原始值:
SELECT ts_col, dt_col, HEX(ts_col), HEX(dt_col) FROM test_table ORDER BY id DESC LIMIT 1;
如果 ts_col(TIMESTAMP)和 dt_col(DATETIME)显示时间相同,且 HEX() 结果不同(说明存储编码不同),基本可确认时区链路通畅。若两者时间差 8 小时,说明至少有一个环节还在用 UTC。
时区问题从来不是单点配置能一劳永逸的,服务端、连接参数、字段类型、应用层解析,四者必须对齐——漏掉任何一环,8 小时偏差就会在某个深夜日志里突然冒出来。











