MySQL CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 在事务中更新时机,是以代码执行时间为准还是以事务提交时间为准?

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 各自取自己的时间
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值