必须用TIMESTAMP仅当明确依赖自动时区转换且接受1970–2038年限制;其余场景默认选DATETIME,因其无时区干扰、无2038年风险、存查一致、范围广至9999年。

DATETIME 和 TIMESTAMP 看似都能存时间,但选错一个,上线后就可能遇到时区错乱、2038年数据截断、跨环境查询结果不一致等问题——不是语法报错那种“立刻发现”的错误,而是静默错,查日志都难定位。
什么时候必须用 TIMESTAMP?
只有一种情况真正需要它:你明确依赖「自动时区转换」且能接受它的硬性限制。
- 用户在东京下单,后台在硅谷查订单时间,希望直接看到
2026-06-09 15:30:00(本地时间),而不是手动做CONVERT_TZ()转换 - 日志表里记录事件发生时刻,所有服务节点部署在不同时区,但你希望
SELECT created_at直接返回当前会话所在时区的可读时间 - 你确认业务时间全部落在
1970-01-01 00:00:01到2038-01-19 03:14:07UTC 范围内(比如内部系统、短期项目)
注意:TIMESTAMP 的自动转换只发生在查询/插入时,底层永远存 UTC;如果应用层自己做了时区转换再写入,反而会导致双重转换、时间偏移。
为什么大多数业务该默认选 DATETIME?
DATETIME 是更可控、更可预期的选择,尤其当你的团队没有统一时区管理能力时。
- 存什么就是什么:插入
'2026-06-09 14:05:00',无论数据库time_zone是+08:00还是SYSTEM,查出来永远是这个值 - 无 2038 年风险:支持到
9999-12-31,生日、合同截止日、历史归档时间全都能放 - MySQL 5.6.5+ 已支持
DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP,自动初始化/更新不再专属TIMESTAMP - 小数秒精度完全一致:两者都支持最多 6 位微秒(
DATETIME(6)/TIMESTAMP(6))
常见误判:“我们有海外用户,所以要用 TIMESTAMP”——其实只要在应用层统一用 UTC 时间写入 DATETIME,前端按用户时区渲染,逻辑更清晰、调试更简单。
TIMESTAMP 容易踩的坑
这些不是理论风险,是线上真实高频问题:
- 服务器时区从
CST(中国标准时间)改成SYSTEM后,老数据查出来时间“跳变”——因为TIMESTAMP读取时按新时区转,但原始写入时用的是旧时区规则 - 备份恢复到另一台时区配置不同的机器,
SELECT结果和原库不一致,但CHECKSUM却显示数据相同(底层 UTC 值没变,只是展示变了) -
INSERT INTO t (ts_col) VALUES (NULL)会自动设为当前时间,而DATETIME插NULL就是NULL;这个隐式行为在批量导入或 ORM 映射时容易漏掉 - 使用
mysqldump --skip-tz-utc备份时,TIMESTAMP字段可能被导出成带时区偏移的字符串,还原时若目标库时区不同,值就错了
性能与空间差异其实没那么关键
别被“TIMESTAMP 只占 4 字节,DATETIME 占 8 字节”带节奏:
- 单行省 4 字节,在千万级表里也就多占几十 MB,远不如索引设计、查询写法影响大
- 两者在 B+ 树索引中的比较效率几乎无差别;所谓“
TIMESTAMP查询更快”是过时机型的误解 - 如果真卡空间,优先考虑压缩行格式(
ROW_FORMAT=COMPRESSED)、归档冷数据,而不是拿时间类型冒险
真正该警惕的,是那些不会报错、但会让“同一笔订单在不同人眼里发生时间不一样”的隐性问题——这种问题往往要等跨国审计或对账时才暴露,修复成本远高于建表时多敲几个字母。


















