ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

悲观锁与乐观锁

悲观锁与乐观锁 悲观锁和乐观锁是两种截然不同的并发控制思想悲观锁假设冲突必然发生操作前先加锁以确保独占资源乐观锁假设冲突概率较低先执行操作仅在提交时校验数据一致性。选择依据核心在于业务场景的冲突频率写多读少强一致性要求高的场景用悲观锁读多写少冲突较少的场景用乐观锁。一、核心定义与思想1. 悲观锁核心思想假设并发冲突一定会发生因此在访问数据前主动加锁确保其他线程/事务无法同时修改数据。行为模式“先锁后操作”类似“锁门上厕所”必须拿到锁才能继续执行。典型场景银行转账、库存扣减、秒杀等强一致性要求高、写操作频繁的场景。2. 乐观锁核心思想假设并发冲突很少发生操作时不加锁仅在提交更新时检查数据是否被修改过。行为模式“先操作后验证”类似“超市自助结账”冲突时重试或报错。典型场景商品浏览、点赞计数等读多写少、冲突概率低的场景。3. 两者对比对比维度悲观锁乐观锁核心思想假设冲突必然发生操作前强制加锁以确保独占资源假设冲突很少发生先执行操作提交时校验数据一致性加锁时机读取数据时立即加锁如SELECT ... FOR UPDATE不加锁仅在提交更新时校验如版本号比对实现方式数据库行锁FOR UPDATE、synchronized、ReentrantLock版本号机制、CAS 算法、时间戳校验并发性能较低锁竞争导致阻塞高并发下吞吐量下降较高无锁设计冲突少时吞吐量显著提升一致性保障强一致性ACID 事务级别绝对避免脏写最终一致性冲突时需重试短暂不一致可接受失败处理阻塞等待锁释放可能超时提交失败需重试或报错如返回“数据已被修改”提示典型风险死锁、锁等待超时、长事务性能瓶颈ABA 问题、高冲突下重试风暴CPU 消耗激增适用场景- 写多读少如银行转账、库存扣减-冲突率 40%-强一致性要求金融交易- 读多写少如点赞计数、配置更新-冲突率 20%-可容忍短暂不一致代码复杂度较低依赖数据库或 JVM 原生支持较高需自行实现重试逻辑、冲突合并策略二、实现机制1. 悲观锁的实现数据库层面通过SELECT ... FOR UPDATE排他锁或SELECT ... FOR SHARE共享锁显式加锁需在事务中使用。必须确保索引命中否则可能锁全表如 MySQL 中未走索引的FOR UPDATE会锁整张表。Java 语言层面synchronized关键字、ReentrantLock等独占锁机制线程竞争时会阻塞等待。数据库层面和Java层面仅需选择一种FOR UPDATE适用于分布式部署的多服务器synchronized适用于单服务器2. 乐观锁的实现版本号机制表中增加version字段更新时校验版本号是否匹配UPDATEproductsSETstockstock-1,versionversion1WHEREid1ANDversion旧值;若影响行数为 0说明数据已被修改需重试或报错。CASCompare and Swap通过 CPU 原子指令实现如 Java 的AtomicIntegerAtomicIntegercountnewAtomicInteger(0);count.incrementAndGet();// 底层通过 CAS 重试实现需注意 ABA 问题值被修改后又恢复可通过AtomicStampedReference添加版本戳解决。三、关键对比1. 适用场景悲观锁更适合写多读少如订单支付、库存扣减。冲突概率高20%或强一致性要求严格如金融交易。临界区执行时间短避免长时间锁持有。乐观锁更适合读多写少如商品详情页、用户配置更新。冲突概率低20%且能容忍短暂不一致。高并发场景避免锁竞争导致的性能瓶颈。2. 性能与风险维度悲观锁乐观锁并发性能低阻塞等待吞吐量受限高无锁竞争冲突少时效率更优一致性保障强一致性ACID 事务级别最终一致性需处理冲突重试失败处理阻塞直至获取锁提交失败需重试或报错典型风险死锁、锁等待超时ABA 问题、高冲突下重试风暴3. 悲观锁的陷阱死锁风险多资源交叉加锁时易发生如事务 A 锁资源 X 后申请 Y事务 B 锁 Y 后申请 X。规避按固定顺序加锁、设置锁超时时间。性能瓶颈未走索引的FOR UPDATE会锁全表MySQL 中常见问题。规避确保查询条件命中索引避免全表扫描。4. 乐观锁的陷阱ABA 问题值被修改后恢复原状导致校验通过但逻辑错误如库存从 10→5→10。规避用版本号替代时间戳自增版本号可追溯修改次数。重试风暴高冲突场景下重试逻辑可能耗尽 CPU 资源。规避限制最大重试次数如 3 次、采用指数退避算法调整重试间隔。四、实际应用建议1. 选择原则冲突频率是核心指标若冲突率40%悲观锁更高效避免频繁重试消耗 CPU。若冲突率20%乐观锁性能优势显著吞吐量可提升 5-10 倍。业务一致性要求金融级操作必须用悲观锁非核心数据如浏览量可用乐观锁。2. 混合策略动态切换监控冲突率低冲突时用乐观锁高冲突时自动降级为悲观锁。分层设计入口层用乐观锁过滤大部分请求。核心层用悲观锁保障关键事务。3. 常见误区乐观锁性能一定更好否。高冲突场景下乐观锁的重试开销可能超过悲观锁的阻塞成本。synchronized 是纯悲观锁否。JVM 会自适应初始用轻量级锁类似乐观策略竞争激烈时升级为重量级锁。五、典型示例1. 库存扣减悲观锁BEGIN;SELECTstockFROMproductsWHEREid1FORUPDATE;-- 先加锁IFstock0THENUPDATEproductsSETstockstock-1WHEREid1;ENDIF;COMMIT;2. 库存扣减乐观锁-- 查询时获取版本号SELECTstock,versionFROMproductsWHEREid1;-- 更新时校验版本UPDATEproductsSETstockstock-1,versionversion1WHEREid1ANDversion旧值;
返回列表