MyBatis 中 SELECT ... FOR UPDATE 未锁住行,根本原因是事务未开启或隔离级别不支持;必须配合 @Transactional、InnoDB 引擎及合适隔离级别,并确保 SQL 正确书写且条件命中索引。

MyBatis 中 SELECT ... FOR UPDATE 为什么没锁住行?
根本原因通常是事务未开启或隔离级别不支持。MyBatis 本身不控制事务边界,FOR UPDATE 只在数据库事务内生效,且依赖存储引擎(如 InnoDB)和隔离级别(推荐 REPEATABLE READ 或 READ COMMITTED)。
- Spring 项目中必须确保方法被
@Transactional注解包裹,且传播行为不是SUPPORTS或NOT_SUPPORTED - MySQL 5.7+ 默认隔离级别是
REPEATABLE READ,但若应用显式设为READ UNCOMMITTED,FOR UPDATE会失效 - MyBatis 的
<select>语句需明确写SELECT * FROM user WHERE id = #{id} FOR UPDATE,不能靠拦截器或注解自动加锁 - 注意:若查询条件未命中索引(比如用了函数、模糊前缀
LIKE '%xxx'),InnoDB 可能升级为表锁,反而拖慢并发
乐观锁用 version 字段时,MyBatis 更新失败却不报错?
这是设计使然——乐观锁的本质是“检测冲突后放弃更新”,不是抛异常。MyBatis 的 update 方法返回值是 int(影响行数),必须主动判断是否为 0。
- SQL 需写成:
UPDATE order SET status = #{status}, version = version + 1 WHERE id = #{id} AND version = #{version} - Java 层必须检查返回值:
if (mapper.updateOrder(order) == 0) { throw new OptimisticLockException("版本冲突"); } - 别把
version字段设为NULL默认值,否则首次更新时WHERE version = NULL永远不成立(要用IS NULL,但和自增逻辑冲突) - 如果用 MyBatis-Plus,
UpdateWrapper的eq("version", entity.getVersion())是安全的,但记得调用setEntityVersion()或手动维护
FOR UPDATE 和 version 方案性能差异在哪?
不是“谁更快”,而是“谁更扛得住”。悲观锁靠数据库行锁阻塞并发,乐观锁靠重试应对冲突,二者适用场景完全不同。
- 高冲突低频写(如秒杀库存扣减):
FOR UPDATE更稳,但连接池容易耗尽;需配合超时(lock_wait_timeout)和快速释放事务 - 低冲突高频写(如用户资料修改):
version几乎无锁开销,但若真发生冲突(比如两人同时编辑),得重试或提示用户刷新 - MyBatis 中混合使用常见:查详情用
FOR UPDATE锁住关键行,再校验业务规则;非核心字段更新走version避免长事务 - 注意:MySQL 的
FOR UPDATE在 RR 级别下可能触发间隙锁,导致无关记录也被锁住,测试时务必用真实数据分布验证
MyBatis 动态 SQL 里拼 FOR UPDATE 容易出什么问题?
最典型的是语法错误或锁范围失控——因为 FOR UPDATE 必须紧贴 SELECT 末尾,不能被 <if> 拆开,也不能出现在子查询里。
- 错误写法:
<if test="forUpdate">FOR UPDATE</if>→ 生成SELECT ... WHERE ... FOR UPDATE是对的,但若forUpdate=false,结尾多出空格或换行可能让 SQL 解析失败 - 正确做法:整个 SQL 语句按需生成,用
<trim suffix="FOR UPDATE">或直接两个独立<select>标签(selectForUpdate/selectNormal) - 切勿在
<foreach>循环中对多 ID 加锁:SELECT ... WHERE id IN (...) FOR UPDATE会锁住所有匹配行,但若部分 ID 不存在,InnoDB 仍可能锁住间隙,引发意外阻塞 - MyBatis 3.4+ 支持
useGeneratedKeys和FOR UPDATE共存,但 Oracle 不支持,跨库时别默认套用
真正难的从来不是写对一行 FOR UPDATE 或加个 version 字段,而是想清楚这笔更新背后有没有其他服务也在改同一行、事务里混没混业务查询、下游会不会因锁等待超时而降级失败——这些没法靠框架自动兜底。

















