乐观锁和悲观锁
乐观锁(Optimistic Concurrency Control, OCC)与悲观锁(Pessimistic Locking, PLC)都是为了解决并发修改导致的数据冲突与“丢失更新”问题,只是对“会不会冲突”这个判断的态度不同:悲观锁默认“很可能冲突”,先上锁再操作,典型做法是在数据库里用行级独占锁(如 SELECT ... FOR UPDATE
)或在代码里用互斥量(synchronized
、ReentrantLock
)把临界区包住,从拿到锁到提交期间阻塞其他写入;乐观锁默认“通常不冲突”,先读后改但不加写锁,提交时校验“我读时看的版本是否仍然有效”,常见做法是在表里加 version
/时间戳列或哈希签名,更新时使用 UPDATE ... SET v=v+1 ... WHERE id=? AND version=?
,受影响行数为 0 则说明有人抢先改了,需要重读并重试。两者都能在事务内配合隔离级别与索引生效,乐观锁通常与 MVCC(多版本并发控制)天然契合以实现快照读,悲观锁则更依赖底层锁管理器(行锁、间隙锁、死锁检测)来提供强一致的串行化效果。
相同点:目标一致——保护数据一致性与隔离性、避免丢失更新;都可能让业务重试(乐观锁在提交时失败重试,悲观锁在等待锁或死锁回滚后重试);都需要良好的主键/索引与清晰的事务边界设计。不同点(从工程视角):获取时机——悲观锁在读/写前就锁,乐观锁在提交时校验;代价模型——悲观锁以“等待/阻塞”为主(可导致锁竞争、长事务放大影响、死锁),乐观锁以“失败/重试”为主(在高冲突场景重试过多);吞吐与延迟——读多写少、短事务、热点不高时乐观锁吞吐更高,写多、高冲突、强一致读改写(如“读后立刻必须保证唯一性”)时悲观锁更稳;故障形态——悲观锁要关注死锁与锁超时,乐观锁要设计幂等与指数回退重试;可扩展性——分布式场景下乐观锁易扩展(版本校验是本地判断),悲观锁若跨节点需额外的分布式锁(如基于数据库选主、ZooKeeper、Redis),并要处理时钟与持久化问题。与数据库隔离级别的关系:MVCC 提供快照一致性读,不能自动避免写写冲突,乐观锁的版本校验负责兜底;而悲观锁通过 FOR UPDATE
/LOCK IN SHARE MODE
等把潜在修改者串行化,代价是阻塞与锁范围管理(如 InnoDB 的间隙锁在可重复读下防止幻读但可能扩大锁冲突面)。