mysql触发器无法实现可靠乐观锁,因其不能前置拦截update、无法控制where条件构造、无法阻止已加锁的更新执行,且无法感知应用层是否传入正确version值。

不能靠触发器实现可靠的版本锁。必须在应用层显式传入 version 字段,并在 UPDATE 的 WHERE 条件中比对,再检查影响行数是否为 1。
为什么触发器无法替代 WHERE version 校验
触发器在 BEFORE UPDATE 阶段能读 OLD.version,但拿不到应用层没传的 NEW.version——如果 ORM 自动生成 SQL 时跳过 version 字段,NEW.version 就是 NULL 或默认值,整个比对失效。更关键的是:触发器无法阻止一条已构造完成的 UPDATE 执行,它只能修改新值或报错,但此时行锁已加、事务已开始,覆盖风险早已发生。
常见错误现象包括:
- 日志里没报错,但数据被静默覆盖
- 多个并发请求都拿到同一个
version值,触发器认为“合法”,全部更新成功 - 触发器强行自增
version,导致后续所有请求都因NEW.version != OLD.version + 1被拒
真正起作用的乐观锁写法(SQL Server)
应用层必须控制三件事:传 version、写进 WHERE、查影响行数。数据库只负责执行和返回结果。
示例语句:
UPDATE order_info SET status = 'shipped', version = version + 1 WHERE id = @id AND version = @expected_version;
执行后检查 @@ROWCOUNT:
- 等于 1 → 更新成功,
version已自增 - 等于 0 → 并发冲突,当前行已被他人更新,
version不匹配
注意:version 字段不能设 DEFAULT 或 ON UPDATE,否则比对逻辑被破坏;索引要覆盖 (id, version),避免全表扫描。
如果硬要用触发器,有哪些前提条件
仅当满足全部以下条件时,BEFORE UPDATE 触发器才可能勉强用于拦截:
- 所有写入口统一走存储过程,禁止 ORM 直连或客户端直写
- 每次
UPDATE都显式包含version字段(哪怕只是回传原值) - 触发器逻辑是:若
NEW.version != OLD.version则允许;否则SIGNAL中断 -
version字段类型为INT或BIGINT,无默认值、无自动更新
即便如此,仍比不上直接在 WHERE 中校验来得简单、透明、可测。触发器增加调试成本,且无法规避应用层漏传字段的问题。
最易被忽略的一点:UPDATE 语句里漏了 version 的 WHERE 条件,哪怕触发器写得再严密,也完全无效。










