mysql乐观锁本身不产生冲突等待,update where version = ? 影响行为0是预期失败;若出现等待,实为混用select for update、where未走索引或事务未及时提交所致。

MySQL 本身不会为乐观锁产生“冲突等待”——出现等待,说明你实际用的是悲观锁逻辑,或者误配了事务隔离级别、索引或重试机制。
UPDATE WHERE version = ? 影响行为为 0,不是锁等待,是预期失败
乐观锁的核心动作是 UPDATE ... WHERE id = ? AND version = ?。InnoDB 执行这条语句时:
- 先定位到
id对应的行(依赖主键或唯一索引,否则会全表扫描加锁) - 读取该行当前
version值,与 WHERE 中传入的旧值比对 - 不相等 → 直接返回影响行数 0,**不加任何锁,不阻塞其他事务**
- 相等 → 更新数据 +
version = version + 1,仅对这一行加 X 行锁(极短,语句级)
所以如果你看到 Lock wait timeout exceeded 或 SHOW PROCESSLIST 里有长时间 Updating 状态,那一定不是乐观锁本身导致的,而是下面某个环节出了问题。
为什么你感觉“像在等待”,其实是这三处卡住了
常见假性“乐观锁等待”真实来源:
-
SELECT ... FOR UPDATE混用:应用层为了“保险”在更新前又加了悲观锁,比如先SELECT stock, version FROM product WHERE id = 1 FOR UPDATE,这就直接触发行锁排队 - WHERE 条件没走索引:
UPDATE product SET ... WHERE version = 123(没带主键),InnoDB 扫描全表判断每行 version,过程中对所有扫描行加临时锁,其他更新全被堵住 - 事务未及时提交:乐观锁更新成功后,事务还在执行后续逻辑(如调第三方接口、复杂计算),导致行锁迟迟不释放,后续所有乐观/悲观更新都得等它
高并发下 version 字段更新引发的隐性瓶颈
看似无锁,但高频更新同一行的 version 字段仍会带来压力:
- 每次更新都触发聚簇索引页的修改,可能引起页分裂(尤其
version是INT但频繁 +1) - 如果业务用了
READ COMMITTED隔离级别,MVCC 版本链变长,UPDATE前需清理旧版本,增加 CPU 开销 - 多个线程同时重试(比如 10 个请求争抢同一商品库存),会在短时间内发出大量
UPDATE ... WHERE version = X,虽然不锁等待,但造成 CPU 和 IOPS 尖峰,DB 响应延迟上升,看起来像“卡住”
真正要警惕的不是“乐观锁等待”,而是把乐观锁当摆设——比如更新失败后不重试、重试没加退避、或重试次数无上限导致雪崩。这些不会报锁错误,但会让业务成功率断崖下跌。











