MySQL中Datetime与Timestamp哪种类型更适合存储时间点信息?
引言
我知道此前已有类似问题被提出,但针对我的使用场景仍需进一步测试,且测试结果让我有些惊讶和困惑。以下是测试过程(含代码)及结果说明。
我正在开发的应用具备以下特性:
- 记录事件历史(某一时间点发生的事件):
- 历史记录不可变,一旦写入便不再修改;
- 需基于该历史记录生成报表。
- 调度未来的可重复任务,例如“每周五执行该检查”。
因此,准确理解时间数据至关重要。我正尝试了解MySQL中的Timestamp和Datetime类型,以及它们与Java代码、ISO8601时间字符串的交互方式。
我查阅了MySQL官方文档中关于Timestamp类型的说明:它会将时间值转换为UTC存储,检索时再转换为服务器或会话时区,这听起来很适合时间点存储。
而Datetime类型的机制相对模糊,有文章认为它与字符串差异不大,我执行SELECT NOW() + 0;查询后得到了与文章一致的结果。
为理清疑问,我编写了一个小型Java类,向包含以下三列的数据库表写入数据:
tz(表示标准时区ID的字符串,测试中使用了UTC,但它并非时区);mydatetime(Datetime类型列);mytimestamp(Timestamp类型列)。
Java代码
我编写的Java代码已上传至代码托管平台,核心逻辑是处理不同时区的ISO8601时间字符串,完成写入MySQL表并读取验证的流程。
测试1
我生成了3个不同的ISO8601时间字符串,它们指向同一时间点但对应不同时区/偏移量,将其写入数据库后再检索并打印。初始时MySQL服务器时区设置为+00:00。
输出结果
写入数据库的时间:
UTC 2018-04-13T11:12:00Z, 1523617920 Europe/Amsterdam 2018-04-13T13:12:00+02:00, 1523617920 Asia/Calcutta 2018-04-13T16:42:00+05:30, 1523617920
从数据库读取的存储时间:
Description, Datetime column (epoch seconds), Datetime column (as ISO string), Timestamp column (as epoch seconds), Timestamp column (as ISO string) UTC, 1523617920, 2018-04-13T11:12:00Z, 1523617920, 2018-04-13T11:12:00Z Europe/Amsterdam, 1523617920, 2018-04-13T13:12+02:00[Europe/Amsterdam], 1523617920, 2018-04-13T13:12+02:00[Europe/Amsterdam] Asia/Calcutta, 1523617920, 2018-04-13T16:42+05:30[Asia/Calcutta], 1523617920, 2018-04-13T16:42+05:30[Asia/Calcutta]
所有结果均正常,将epoch seconds转换后,与ISO8601时间字符串完全一致。
测试2
随后我将MySQL服务器时区从UTC(+00:00)改为Europe/Amsterdam(+02:00),再次读取存储的时间。
输出结果
从数据库读取的存储时间:
Description, Datetime column (epoch seconds), Datetime column (as ISO string), Timestamp column (as epoch seconds), Timestamp column (as ISO string) UTC, 1523617920, 2018-04-13T11:12:00Z, 1523625120, 2018-04-13T13:12:00Z Europe/Amsterdam, 1523617920, 2018-04-13T13:12+02:00[Europe/Amsterdam], 1523625120, 2018-04-13T15:12+02:00[Europe/Amsterdam] Asia/Calcutta, 1523617920, 2018-04-13T16:42+05:30[Asia/Calcutta], 1523625120, 2018-04-13T18:42+05:30[Asia/Calcutta]
我原本以为Datetime列会受影响(因为它不存储时区信息),但实际变化的却是Timestamp列——它的epoch seconds和ISO字符串都发生了偏移,而Datetime列完全保留了写入时的原始信息。
结论与疑问
我们不会频繁更改服务器时区,我希望找到能准确、稳定表示时间点的MySQL数据类型。根据上述测试,若以ISO8601字符串形式传入时间点信息,Datetime类型能保留原始信息,不受服务器时区变更的影响;而Timestamp则会随服务器时区变化自动转换,导致读取的时间值发生偏移。
不过我不确定我的测试代码或结果解读是否存在错误,想请教各位:在我的测试场景(需要存储不可变的时间点、生成报表、调度任务)中,MySQL的Datetime类型是否更适合存储时间点信息?
内容的提问来源于stack exchange,提问作者Eoin McCarthy

