timestamp 自动时区转换且范围1970–2038,适合逻辑时间统一;datetime 无时区转换、范围1000–9999,适合本地时刻或长期时间。

Timestamp 存储时自动转换时区,Datetime 不转
MySQL 的 TIMESTAMP 类型在写入和读取时会根据 time_zone 系统变量做隐式转换:存入时转成 UTC,读取时再转回当前会话时区。而 DATETIME 完全不参与时区转换,存啥读啥。
这意味着如果你的应用部署在多个时区(比如服务器在 UTC+0,但用户全在中国),用 TIMESTAMP 能让同一逻辑时间(如“订单创建时间”)在所有客户端显示一致;但如果你明确需要记录某个本地时刻(比如“会议定在 2024-05-20 14:00 北京时间”,且不希望它被解释成其他时区的 14:00),就得用 DATETIME。
- 检查当前会话时区:
SELECT @@time_zone; - 临时切换时区测试效果:
SET time_zone = '+08:00'; -
TIMESTAMP的自动转换只发生在字段定义为TIMESTAMP且未显式指定NULL或默认值时;加了DEFAULT CURRENT_TIMESTAMP的列也会触发
Timestamp 有年份范围限制(1970–2038),Datetime 更宽(1000–9999)
TIMESTAMP 底层是 4 字节整数,存储的是 Unix 时间戳秒数,所以只能表示从 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC —— 这就是著名的“2038 年问题”。DATETIME 是 8 字节字符串编码,支持 1000-01-01 到 9999-12-31。
如果业务涉及历史档案、长期合约、出生日期、计划截止日远于 2038 年,TIMESTAMP 直接不可用。
- 插入超范围值时,
TIMESTAMP会静默截断为0000-00-00 00:00:00(取决于 SQL mode),查不到数据还难排查 -
DATETIME插入非法日期(如2024-02-30)会报错:Incorrect datetime value - MySQL 5.6.4+ 对
DATETIME支持毫秒精度(DATETIME(3)),TIMESTAMP同样支持,但范围不变
索引效率几乎没差别,但 Timestamp 占用更少空间
单行存储上,TIMESTAMP 固定占 4 字节,DATETIME 占 5–8 字节(取决于是否带微秒)。对千万级以上表,这个差异会反映在内存占用、缓冲池压力和磁盘 I/O 上。
但别指望靠换类型来“优化查询速度”——两者在 B+ 树索引中的比较逻辑、排序行为、范围扫描性能基本一致。真正影响查询效率的是是否建了索引、索引覆盖、WHERE 条件写法,而不是选哪个时间类型。
- 如果字段高频用于
ORDER BY或GROUP BY,优先确保它有索引,类型选择其次 - 使用
EXPLAIN查看执行计划时,type和key_len字段不会因TIMESTAMP/DATETIME变化而显著不同 - 注意:MySQL 8.0+ 的函数索引允许对时间字段做表达式索引(如
YEAR(created_at)),这时类型不影响语法,但语义要对得上
ALTER TABLE 修改类型可能锁表且丢失精度
把 DATETIME 改成 TIMESTAMP,或反过来,在 MySQL 5.7 及以前大概率触发全表拷贝(ALGORITHM=COPY),大表会卡住写入。MySQL 8.0 支持部分 INPLACE 修改,但仍有风险。
更隐蔽的问题是精度丢失:比如原 DATETIME(6) 存了微秒,改成 TIMESTAMP 不带精度声明,就变成秒级;或者时区转换导致值偏移。
- 修改前务必确认当前值是否都在目标类型的合法范围内(特别是
TIMESTAMP的 1970–2038) - 测试环境先跑一遍:
ALTER TABLE t1 MODIFY ts_col TIMESTAMP;,观察警告(SHOW WARNINGS) - 生产环境建议走应用层双写过渡:新增字段、同步写入、校验一致、切查询、删旧字段
真正麻烦的不是选哪个类型,而是团队里有人用 NOW() 插 DATETIME,有人用 UNIX_TIMESTAMP() 算 TIMESTAMP,结果 WHERE 条件跨类型比较,隐式转换让索引失效——这种混合用法比类型本身更伤性能。











