优先选TIMESTAMP存创建/更新时间,因其支持自动更新、时区自适应且省空间;存历史或远期日期(如生日、2038年后时间)必须用DATETIME,避免2038年溢出及时区错乱。

选 DATETIME 还是 TIMESTAMP,关键看三点:是否要跨时区、数据会不会超 2038 年、有没有自动更新需求。其他都是次要的。
存创建/更新时间,优先用 TIMESTAMP
如果你在建日志表、用户表,需要记录 created_at 或 updated_at,TIMESTAMP 是更稳妥的选择:
- 支持
CURRENT_TIMESTAMP默认值和ON UPDATE CURRENT_TIMESTAMP自动更新,MySQL 5.6.5+ 的DATETIME虽然也支持,但早期版本不兼容 - 4 字节存储,比
DATETIME(8 字节)更省空间,索引体积小,对高频写入场景有实际收益 - 底层存 UTC,查的时候自动转成当前会话时区——比如服务器在 UTC,App 用户在东京,查出来就是 +09:00 时间,不用应用层做转换
- 注意:
TIMESTAMP列不能有两个都设DEFAULT CURRENT_TIMESTAMP,否则建表报错Invalid default value for 'xxx'
涉及历史日期、远期截止日,必须用 DATETIME
一旦出现以下任一情况,TIMESTAMP 就不能用:
- 字段要存生日(如
'1985-04-12')、公司成立日('1949-10-01')、合同到期日('2100-12-31')——这些全在TIMESTAMP范围外 - 业务明确要求支持 2038 年之后的时间,比如金融系统里的长期理财到期日、科研项目的实验周期
- 你无法控制数据库时区,或不同环境(开发/测试/生产)时区不一致,又不想让时间值随
time_zone变动而“跳变” -
DATETIME存的是字面值,插进去'2026-09-02 15:30:00',不管会话时区怎么切,查出来永远是这个字符串
时区切换导致时间“变掉”,大概率是误用了 DATETIME
典型现象:本地开发环境显示订单创建时间是 '2026-09-02 15:30:00',上线后变成 '2026-09-02 07:30:00'。这不是 bug,是 DATETIME 在“老实干活”:
- 开发机设了
time_zone = '+08:00',你用NOW()插入,得到的就是东八区时间字符串 - 线上库默认
time_zone = '+00:00',但DATETIME不做任何转换,它就照原样存、照原样读 - 结果就是:你在东八区写的 “下午三点”,到了 UTC 环境里还是显示 “下午三点”,但实际绝对时间已经偏了 8 小时
- 换成
TIMESTAMP就没这个问题:插的时候转 UTC 存,读的时候按当前会话时区转回来,逻辑自洽
性能和存储差异在真实场景中几乎可以忽略
别被“TIMESTAMP 4 字节、DATETIME 8 字节”带偏节奏:
- 单行差 4 字节,百万级表也就多占几 MB,远不如一个没建对的索引影响大
- 索引效率差异只在极端高并发写入时才可能体现,日常业务中感知不到
- 真正要注意的是:如果字段要参与范围查询(比如
WHERE created_at > '2026-01-01'),两者性能完全一致,B+ 树索引行为没区别 - 反而容易踩坑的是精度——两者都支持微秒(
DATETIME(6)/TIMESTAMP(6)),但如果你没显式声明长度,插入带毫秒的时间会被截断,且不报错
最常被忽略的一点:TIMESTAMP 的 2038 年限制不是理论风险,而是现实倒计时——现在已经是 2026 年,离溢出只剩两年多。如果系统生命周期超过这个时间,或者要对接外部系统传来的未来时间戳,DATETIME 是唯一安全选项。


















