前言※
某个惬意的下午,突然接到用户反馈:在平台使用过程中,修改了一些描述信息后,数据的创建时间竟然发生了变化。这种情况显然不符合预期,于是我们迅速展开了问题排查。
问题定位※
接到反馈后,第一反应是怀疑前端同事可能误用了“修改时间”字段,而非“创建时间”。然而,查看数据库中对应的记录后,发现“创建时间”确实发生了变化,排除了前端取值错误的可能性。
接下来进入了代码追踪环节。经过一番梳理,并未发现明显的逻辑问题。此时,事情变得有些蹊跷——这种现象与数据库自动更新时间的行为非常相似。
于是,我们进一步检查了建表语句,果然发现了问题所在:创建时间字段上设置了 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,以利用其更合理的默认配置。
- 定期审查数据库设计:通过定期检查表结构和字段定义,确保其符合业务需求,减少潜在隐患。
参考资料