MySQL踩坑日记 - explicit_defaults_for_timestamp

-
-
2025-03-28

前言

某个惬意的下午,突然接到用户反馈:在平台使用过程中,修改了一些描述信息后,数据的创建时间竟然发生了变化。这种情况显然不符合预期,于是我们迅速展开了问题排查。

 

问题定位

接到反馈后,第一反应是怀疑前端同事可能误用了“修改时间”字段,而非“创建时间”。然而,查看数据库中对应的记录后,发现“创建时间”确实发生了变化,排除了前端取值错误的可能性。

接下来进入了代码追踪环节。经过一番梳理,并未发现明显的逻辑问题。此时,事情变得有些蹊跷——这种现象与数据库自动更新时间的行为非常相似。

于是,我们进一步检查了建表语句,果然发现了问题所在:创建时间字段上设置了 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。这表明,每次记录更新时,该字段都会被自动更新为当前时间。

随后,我们开始追溯建表历史。同事表示,建表时并未显式设置该默认值,且后续也没有任何修改记录。为了验证这一点,我们查阅了相关日志,确认了同事的说法。

 

根因分析

经过深入研究,发现问题的根本原因与 MySQL 5.7 的默认行为有关。

在 MySQL 5.7 中,explicit_defaults_for_timestamp 参数的默认值为 OFF。当字段类型为 TIMESTAMP 且定义为 NOT NULL 时,MySQL 会隐式地为其添加默认值

以下是一个示例验证过程:

mysql> select version();
+-----------------------------+
| version()                   |
+-----------------------------+
| 5.7.16-0ubuntu0.16.04.1-log |
+-----------------------------+

mysql> show variables like "%explicit_defaults_for_timestamp%";
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| explicit_defaults_for_timestamp | OFF   |
+---------------------------------+-------+
    
mysql> create table t3 (
    -> id int not null,
    -> d1 timestamp not null,
    -> d2 timestamp not null,
    -> num double not null)
    -> engine=innodb;
Query OK, 0 rows affected (0.31 sec)

mysql> show create table t3;

  CREATE TABLE `t3` (
  `id` int(11) NOT NULL,
  `d1` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `d2` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `num` double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

 

从上述结果可以看出,即使未显式指定默认值,MySQL 5.7 也会为 TIMESTAMP 类型的字段隐式添加默认行为。

需要注意的是,在 MySQL 8.x 版本中,explicit_defaults_for_timestamp 的默认值已调整为 ON,因此不会再出现类似问题。

 

总结与建议

此次问题的根本原因是 MySQL 5.7 的隐式默认行为导致的时间字段自动更新。为了避免类似情况再次发生,建议采取以下措施:

  • 显式定义字段属性:在建表时,明确指定每个字段的默认值和行为,避免依赖数据库的隐式规则。
  • 升级 MySQL 版本:如果条件允许,可以考虑升级到 MySQL 8.x,以利用其更合理的默认配置。
  • 定期审查数据库设计:通过定期检查表结构和字段定义,确保其符合业务需求,减少潜在隐患。

 

参考资料

MySQL 5.7

MySQL 8.0

Why MySQL 5.7 timestamp not null requires default value?


目录