DATETIME 与 TIMESTAMP 的选择
基本区别
| 特性 | DATETIME | TIMESTAMP |
|---|---|---|
| 存储空间 | 5 字节(8.0)+ 小数秒 | 4 字节 + 小数秒 |
| 取值范围 | 1000-01-01 ~ 9999-12-31 | 1970-01-01 00:00:01 ~ 2038-01-19 03:14:07 |
| 时区处理 | 不处理,存什么取什么 | 存储时转为 UTC,查询时转回会话时区 |
| 自动初始化 | 需手动指定 DEFAULT | 支持 DEFAULT CURRENT_TIMESTAMP |
| 自动更新 | 需手动指定 ON UPDATE | 支持 ON UPDATE CURRENT_TIMESTAMP |
最重要的问题:2038 年问题
TIMESTAMP 使用 4 字节存储,以秒为单位从 1970-01-01 00:00:00 UTC 开始计数,最大值为 2038-01-19 03:14:07 UTC。
这是一个真实的 Y2K 级别的兼容性问题。存储超过该时间的时间戳时,TIMESTAMP 会溢出。
DATETIME 没有这个限制,它的范围到公元 9999 年。
时区处理差异
这是两者最核心的行为区别:
-- 假设会话时区为 '+08:00'(北京时间)
INSERT INTO t VALUES ('2025-06-15 10:00:00');
-- DATETIME 列:直接存储 2025-06-15 10:00:00
-- TIMESTAMP 列:转为 UTC 存储 → 2025-06-15 02:00:00(-8小时)
-- 切换会话时区
SET time_zone = '+00:00';
-- DATETIME 列:显示 2025-06-15 10:00:00(不变,因为是插入时的字面值)
-- TIMESTAMP 列:显示 2025-06-15 02:00:00(转为 UTC 显示)
这意味着:
– TIMESTAMP 适合”记录一个绝对时刻”(如订单创建时间)
– DATETIME 适合”记录一个日历时间”(如生日、会议开始时间——不应受时区改变)
存储空间和性能
从 MySQL 5.6.4 开始,DATETIME 的存储从 8 字节改为 5 字节(不含小数秒),与 TIMESTAMP 的差距缩小了:
- TIMESTAMP:4 字节(精确到秒)
- DATETIME:5 字节(精确到秒)
- 带小数秒(如 3 位毫秒精度):各加 1 字节
性能上两者差异极小。TIMESTAMP 因为有时区转换需要额外 CPU,但现代 MySQL 下基本可忽略。真正的性能差异来自:
– 索引大小:TIMESTAMP 更小,索引页能缓存更多行
– 分区表:部分场景下 TIMESTAMP 做分区键更方便
自动初始化与自动更新
CREATE TABLE events (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
DATETIME 从 MySQL 5.6.5 起也支持 DEFAULT 和 ON UPDATE,语法相同:
CREATE TABLE events (
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
所以”TIMESTAMP 才能自动设当前时间”已经是旧观念了。
小数秒精度
两者都支持 fractional seconds(小数秒),精度可达微秒:
CREATE TABLE t (
a TIMESTAMP(3), -- 毫秒级精度
b DATETIME(6), -- 微秒级精度
c TIMESTAMP(0) -- 精确到秒(默认)
);
选择策略
优先使用 DATETIME 的场景
- 未来时间:超过 2038 年的日期(如预约系统)
- 日历日期:生日、纪念日、固定节假日
- 不受时区影响的场景:如”每天 8 点上班”不应受时区变化影响
- 跨时区应用:当需要”记住用户输入的原始时间”时
优先使用 TIMESTAMP 的场景
- 事件时间戳:日志时间、创建时间、更新时间(绝对时刻)
- 全球应用:自动处理时区转换,用户看到自己时区的时间
- 空间敏感:需要节省几字节的场景
- 时间范围在 1970-2038 内:大部分在线业务场景
安全选择:BIGINT 存时间戳
如果两个都纠结,另一种方案是用 BIGINT 存 Unix 时间戳(毫秒或微秒级):
– 无 2038 年问题
– 应用层完全控制时区逻辑
– 排序、比较效率最好
– 但 SQL 中可读性差,调试不便
现代推荐:DATETIME
在 MySQL 8.0 中,DATETIME 和 TIMESTAMP 的功能差距已经基本抹平。考虑到 2038 年问题,默认选择 DATETIME(3) 是更稳妥的实践,存储占用也只有 6 字节(带毫秒)。TIMESTAMP 仅在明确需要自动时区转换且确认时间不会超过 2038 年时使用。
面试常问题
Q:TIMESTAMP 存 ‘2025-06-15 10:00:00’ 在东八区读出来是什么?
A:也是 2025-06-15 10:00:00(前提是会话时区也是东八区)。如果会话切到 UTC,则显示 2025-06-15 02:00:00。
Q:现在 DATETIME 存 5 字节够吗?
A:够。5 字节存到 9999 年,精度到秒。MySQL 8.0 使用紧凑存储。
Q:线上可以从 TIMESTAMP 迁移到 DATETIME 吗?
A:可以,但注意:现有数据以 UTC 存储,迁移时需确保转换正确。建议先 ALTER TABLE 试跑,验证数据一致性。


暂无评论内容