MySQL 兩大時間類型 DATETIME 與 TIMESTAMP 外觀相似(均顯示 YYYY-MM-DD HH:MM:SS),但底層儲存、時區處理、取值範圍、自動更新行為差異巨大。選型錯誤會導致跨時區業務錯誤、2038 年問題、查詢結果不一致。本文深度對比,給出明確選型建議。
| 維度 | DATETIME | TIMESTAMP |
|---|---|---|
| 儲存空間 | 8 字節 | 4 字節 |
| 儲存格式 | 原始值直存(YYYYMMDDHHMMSS) | UTC 時間戳(秒數,1970 起) |
| 時區感知 | 無(存什麼取什麼) | 有(寫入轉 UTC,讀取轉回會話時區) |
| 取值範圍 | 1000-01-01 ~ 9999-12-31 | 1970-01-01 00:00:01 ~ 2038-01-19 03:14:07 UTC |
| 2038 年問題 | 無 | 有(32 位有符號整數上限) |
| 自動更新 | 無(需觸發器/應用層) | 支援 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
| 零值 | '0000-00-00 00:00:00' | '0000-00-00 00:00:00'(或 NULL) |
| 索引效能 | 略大,範圍查詢同等 | 略小,範圍查詢同等 |
-- 會話時區設為 UTC
SET time_zone = '+00:00';
INSERT INTO t (ts, dt) VALUES ('2024-01-15 12:00:00', '2024-01-15 12:00:00');
-- 切換到東八區
SET time_zone = '+08:00';
SELECT ts, dt FROM t;
-- 结果:
-- ts: 2024-01-15 20:00:00 (自動轉換)
-- dt: 2024-01-15 12:00:00 (原值不變)
結論:TIMESTAMP 隨時區變化而變化,DATETIME 固定不變。
time_zone 變更影響DEFAULT CURRENT_TIMESTAMP 也能實現自動初始化created_at、updated_at、操作日誌CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
-- 業務時間:用戶看到的下單時間,固定不變
order_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- 系統時間:內部記錄、審計、排序,自動維護
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
WHERE DATE(ts) = ... 失效索引,改用範圍查詢mysqldump --tz-utc(預設開啟)確保 TIMESTAMP 正確轉換time_zone = '+00:00',應用層處理顯示轉換sql_mode=NO_ZERO_DATE,NO_ZERO_IN_DATE 禁止零值DATETIME(6)、TIMESTAMP(6) 支援微秒精度TIMESTAMPADD/TIMESTAMPDIFF 跨單位計算更便捷理解兩者本質差異,按業務語義選型,是避免時間相關 Bug 的關鍵。現代應用建議:業務時間用 DATETIME,系統時間用 TIMESTAMP,雙欄位並存最安心。