mysql中乐观锁和悲观锁是基于不同并发假设的控制策略:悲观锁用select for update加行级排他锁,需事务且依赖索引;乐观锁通过version字段在update中校验,靠row_count()判断冲突,适用于读多写少场景。

MySQL 中乐观锁和悲观锁不是数据库内置的两种“开关式”功能,而是基于不同并发假设设计的控制策略。它们的实现方式、适用条件和代码结构差异明显,关键不在“能不能用”,而在“该不该用”以及“怎么用对”。
悲观锁:用 SELECT FOR UPDATE 加行级排他锁
悲观锁在 InnoDB 引擎中通过 SELECT ... FOR UPDATE 实现,本质是事务内对目标行加 X 锁(排他锁),其他事务无法修改或再次加锁,直到当前事务提交或回滚。
- 必须显式开启事务(BEGIN 或 START TRANSACTION),自动提交(autocommit=1)下无效
- 加锁效果取决于 WHERE 条件是否命中索引:主键/唯一索引 → 行锁;无索引字段 → 退化为表锁,严重降低并发能力
- 锁在 COMMIT 或 ROLLBACK 后才释放,长时间事务会阻塞他人,需避免业务逻辑卡在锁内
- 典型场景如秒杀扣库存、银行转账:先查余额并锁住账户,再做校验与更新,确保中间状态不被篡改
乐观锁:靠版本号或 CAS 字段做更新时校验
乐观锁没有真正“加锁”,而是应用层配合数据库,在 UPDATE 语句中嵌入一致性检查条件,依赖 MySQL 的原子性执行结果判断是否成功。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 常见做法是在表中增加 version(整型递增)或 updated_at(时间戳)字段
- 读取数据时一并获取当前 version 值;更新时写成 UPDATE t SET x=..., version=version+1 WHERE id=? AND version=?
- 执行后检查 ROW_COUNT():若为 0,说明 version 已变,发生并发冲突,需业务层决定重试或提示失败
- 不依赖事务隔离级别,也不阻塞其他读写,适合读多写少、冲突概率低的场景(如用户资料编辑、订单状态变更)
选型关键看业务特征,不是技术炫技
悲观锁强一致但吞吐受限,乐观锁高并发但需容忍失败。真实系统中常混合使用:
- 库存扣减初期用悲观锁防超卖,下单成功后用乐观锁更新订单状态
- 高热商品详情页用缓存+乐观锁,冷门商品直接走数据库读
- 金融类核心账户操作必须用悲观锁;后台管理类低频更新可默认用乐观锁
- 注意:乐观锁失败重试不能无限制,要设最大次数+降级策略(如转人工审核)
避坑提醒:看似简单,实则细节决定成败
两个锁机制都容易因配置或写法出错而失效:
- FOR UPDATE 忘记 BEGIN → 实际没进事务,锁立即释放,形同虚设
- WHERE 条件没走索引 → 行锁变表锁,整个商品表被卡住
- 乐观锁 version 字段未设默认值或允许 NULL → 第一次更新就因条件恒假失败
- UPDATE 语句漏写 AND version=? → 变成无条件覆盖,彻底失去并发保护










