MySQL update_time 在事务中到底何时更新?许多开发者都忽略的关键细节
在日常开发中,我们经常给表加上这样的字段,用来追踪数据的最新更新时间:
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
这看似简单,但当你的 SQL 运行在一个耗时较长的事务里时,一个关键问题就变得很重要:
MySQL 的
update_time是在 UPDATE 语句执行那一刻更新,还是等事务 COMMIT 时才更新?
比如:
- UPDATE 开始执行的时间是 14:54:00
- 事务因为复杂逻辑或锁等待,到 14:54:02 才提交
那么:
update_time最终会记录 14:54:00 还是 14:54:02?
这是开发者、DBA 和排查线上问题时都会遇到的疑问。
本文将给出经过验证的明确答案。
一、结论:update_time 记录的是语句执行时间,而不是提交时间
MySQL(尤其 InnoDB)的行为是:
CURRENT_TIMESTAMP 在 语句执行开始时(statement start time)就确定下来了
而不是等到事务提交。
因此:
- UPDATE 语句执行开始时 = 14:54:00
- MySQL 当时就取到了
CURRENT_TIMESTAMP = 14:54:00 - 即使事务到 14:54:02 才提交,这个时间不会改变
最终写入的值依然是:
2025-xx-xx 14:54:00
二、为什么 MySQL 要这样设计?
这是因为 MySQL 在事务内遵守 语句级时间一致性(statement time consistency)。
也就是说:
- 一个 SQL 语句内部看到的所有 “当前时间” 都必须一致
- 不能让一个语句内部出现多个不同的时间片段
如果 CURRENT_TIMESTAMP 改成在 commit 时执行,会带来:
- 同一事务中多条语句的时间不一致
- 可能违反时间逻辑(例如先更新 A 后更新 B,但 update_time 却相反)
- 影响语句执行计划中的一些可重复读特性
因此 MySQL(以及多数数据库)都采用 语句级时间。
三、实测示例
下面是一个最容易复现的测试:
@Transactional(rollbackFor = Exception.class)
public void test1() {
//打印开始执行时间
System.out.println(DateUtil.date());
//执行更新语句,此时user表的update_time的默认值已被设置为
//CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
userMapper.updateById(new User(){{
setId(1L);
setUserName("张三");
}});
//线程等待5秒
ThreadUtil.sleep(5000);
//打印新的时间,在这个时间之后,事务会被提交
System.out.println(DateUtil.date());
}
执行代码会出现以下打印日志:
2025-11-27 15:23:33
2025-11-27 15:23:38
而数据库中该条数据的updateTime被修改为2025-11-27 15:23:33
四、一个事务中多条 UPDATE 会怎样?
再进一步,如果你在一个事务中连续执行多条 UPDATE:
BEGIN;
UPDATE t SET col=1 WHERE id=1; -- 14:00:00
UPDATE t SET col=2 WHERE id=2; -- 14:00:05
COMMIT; -- 14:00:10
那么各自的 update_time 会是:
- 第一条 →
14:00:00 - 第二条 →
14:00:05
它们会各自独立取自己的「语句开始时间」。
五、如果我真的需要记录“提交时间”怎么办?
这是一个常见需求,例如审计日志、业务对账、分布式系统一致性等。
有几种方案:
方案 1:事务提交后再次更新update_time
在 事务提交后 再UPDATE一次:
UPDATE t SET value = ..., update_time = NOW(6) WHERE ...
方案 2:触发器 BEFORE UPDATE/INSERT
可确保统一控制:
CREATE TRIGGER tg_update_time BEFORE UPDATE ON t
FOR EACH ROW SET NEW.update_time = NOW(6);
方案 3:MySQL 8.0+ 提供 transaction_timestamp()
但它表示的是 事务开始 时间,而不是提交时间。
若你特别需要“提交时刻”,则必须由业务或中间件层控制。
六、总结
MySQL 中 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 的时间并不是在事务提交时更新,而是在:
UPDATE 语句开始执行的那一刻就确定了。
这意味着:
- 长事务不会导致
update_time推迟 - 锁等待不会改变
update_time - 同一事务中多条 UPDATE 各自取自己的时间

被折叠的 条评论
为什么被折叠?



