必须统一主从default-time-zone为'+08:00'并设binlog_format=row,禁用now()等函数,jdbc需配置servertimezone=asia/shanghai,推荐统一用datetime存utc或+08:00时间。

主库和从库 default-time-zone 不一致,直接导致时间字段写入错乱
这不是延迟问题,而是逻辑错误:主库执行 INSERT INTO t VALUES (NOW()),binlog 记录的是“当前会话时区下的字面值”,从库用自己配置的 time_zone 去解析,结果可能差 1 小时甚至更多。现象是 SHOW SLAVE STATUS\G 显示正常,但查出来的 datetime 字段和业务预期对不上。
必须在 my.cnf 中为主库和从库**都显式设置**:
[mysqld] default-time-zone = '+08:00'
不要用 SYSTEM(绑定系统时区,Docker 或运维改系统时区就崩);也别用 Asia/Shanghai(MySQL 5.7 旧版本支持不稳定,还要额外加载时区表)。配置后重启 MySQL,并确认:
SELECT @@global.time_zone, @@session.time_zone; 返回 +08:00,不是 SYSTEM 或空值。
NOW() 和 CURRENT_TIMESTAMP 在 STATEMENT 格式 binlog 下仍会出错
即使主从 default-time-zone 对齐,只要 binlog_format = STATEMENT,NOW() 这类函数依然危险:主库生成时间字面量时按自身时区,从库执行时再按自己时区解释一次,等于双重转换。
- 强制切换为
binlog_format = ROW:时间值以二进制形式记录,绕过时区解析 - 禁止在写 SQL 中直接使用
NOW()、CURRENT_TIMESTAMP();应用层生成带时区的时间字符串(如2026-06-15T03:10:00+08:00)或 UTC 时间戳存入DATETIME - 如果必须用默认值,字段定义写成
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,并确认explicit_defaults_for_timestamp = ON(MySQL 5.7+ 默认开启)
JDBC 连接串漏配 serverTimezone,让时区问题从数据库蔓延到应用层
Java 应用连 MySQL,光靠服务端设 default-time-zone 不够。JDBC 8.0+ 驱动默认不信任服务端声明,会把 java.util.Date 按 JVM 本地时区转 UTC 再发给 MySQL,而 MySQL 又按自己的 time_zone 解析——两头各转一次,结果偏移 8 小时很常见。
连接串必须显式指定:
jdbc:mysql://host:3306/db?serverTimezone=Asia/Shanghai&useTimezone=true
注意:GMT%2B8 多数驱动不识别,优先用 IANA 名(Asia/Shanghai);Spring Boot 的 spring.datasource.url 里也要包含该参数,spring.jackson.time-zone 对数据库层无效。
TIMESTAMP 字段自动转换,反而放大时区混乱
TIMESTAMP 存的是 UTC,读写都依赖当前会话 time_zone 自动转换;DATETIME 存字面值,完全不转换。很多人以为 “用 TIMESTAMP 就能自动适配”,结果 Java 写入 2026-06-15 03:10:00,MySQL 里变成 2026-06-14 19:10:00——其实是 JDBC 把本地时间当成本地时区转 UTC 存进去,再用另一个时区读出来。
线上系统建议统一用 DATETIME 类型,并明确约定存储时区(比如全部存 UTC,或全部存 +08:00),由业务代码控制含义。别指望 MySQL 自动“智能适配”。
真正要验证是否生效,不能只看 @@time_zone,得用 pt-table-checksum 校验时间列,或手动插入含 NOW() 的语句,再查从库 relay log 回放结果——很多问题只在 binlog 回放那一刻才暴露。











