业务视角:悲观锁与乐观锁该怎么选?
做业务开发时,你是否遇到过这些问题:秒杀活动库存 “超卖”、用户余额扣成负数、订单状态乱跳?这些本质都是并发资源竞争导致的,而悲观锁和乐观锁,就是解决这类问题的两种核心思路。但很多人搞不清:到底什么业务场景该用悲观锁?什么场景适合乐观锁?今天咱们从业务本质出发,把这个问题讲透。
一、先搞懂:悲观锁与乐观锁的核心区别
在聊场景前,先记住一个核心差异 ——对 “并发” 的信任态度不同:
| 锁类型 | 核心思想 | 通俗理解 |
|---|---|---|
| 悲观锁 | 不信任任何并发,先 “锁死资源” 再操作 | “先占坑,再做事,谁也别抢” |
| 乐观锁 | 信任并发,先操作再 “校验一致性” | “先做事,做完再看有没有人捣乱” |
举个生活例子:
- 悲观锁像你去餐厅吃饭,先占好桌子(锁资源),再叫服务员点餐(操作);
- 乐观锁像外卖点单,先下单付款(操作),接单后平台校验库存(一致性校验),没库存就退款。
二、什么时候用悲观锁?3 类核心业务场景
悲观锁的优点是强一致性(操作期间资源绝对安全),缺点是性能损耗大(会阻塞其他请求),所以适合 “一致性优先、并发不高” 的场景。
1. 写操作频繁,且涉及核心资产(数据错误代价极高)
典型场景:金融转账、用户余额修改、还款业务
比如银行 “A 转 100 元给 B” 的业务:
- 流程必须是:查 A 余额≥100 → 扣 A100 → 加 B100
- 若不锁资源,并发情况下可能出现 “A 余额只剩 50,却被两个请求同时扣 100”,导致余额为负(资损风险)。
这时必须用悲观锁:操作 A 账户时先 “锁住 A 的余额记录”,直到整个转账流程结束才释放锁。哪怕并发量低,也要优先保证资金安全 —— 毕竟金融业务中,“数据正确” 比 “性能快 10ms” 重要 100 倍。
2. 长事务场景(操作耗时久,需持续占用资源)
典型场景:分布式事务、复杂订单确认(需跨系统校验)
比如电商 “下单后扣库存 + 减优惠券 + 冻结积分” 的长事务:
- 整个流程可能需要调用 3 个系统接口,耗时 1-2 秒;
- 若用乐观锁,中途可能有其他请求修改库存 / 优惠券,导致最终校验失败,用户反复重试;
- 用悲观锁锁住 “用户的优惠券记录” 和 “商品库存记录”,直到整个事务完成再释放,能避免中间状态被干扰,减少用户操作失败率。
3. 数据冲突概率极高(不锁就会频繁出错)
典型场景:秒杀中的 “库存预占”(非最终扣减)、票务系统选座
比如高铁票 “选座后 30 分钟未支付” 的场景:
- 用户选座后,必须锁定该座位(否则 100 人同时选同一个座);
- 虽然最终支付率可能只有 50%,但 “先锁座再等支付” 是业务必需 —— 总不能让用户选完座,付完钱才发现座位被抢了,体验太差。
三、什么时候用乐观锁?3 类核心业务场景
乐观锁的优点是性能高(不阻塞请求),缺点是可能出现冲突(需处理重试),所以适合 “并发高、一致性要求不极致” 的场景。
1. 读操作频繁,写操作极少(冲突概率低)
典型场景:商品详情页库存展示、用户积分查询、资讯文章点赞
比如电商商品详情页 “剩余 200 件” 的展示:
- 99% 的请求是用户查看库存(读),只有 1% 是用户下单(写);
- 若用悲观锁,每次查看库存都要锁记录,会导致页面加载变慢;
- 用乐观锁:展示时直接读库存,下单时通过 “版本号” 校验(比如库存表加个version字段,下单时判断当前version == 数据库version,一致才扣减),既不影响读性能,又能避免超卖。
2. 高并发场景(秒杀、抢购、限流)
典型场景:双 11 秒杀、直播带货抢购、红包雨
比如 “1 元秒杀 1000 件商品” 的场景:
- 并发量可能达到 10 万 / 秒,若用悲观锁,大量请求会被阻塞在 “等锁” 环节,导致超时失败,用户体验极差;
- 用乐观锁:让所有请求先 “预扣库存”(比如用 Redis 计数),然后异步校验一致性,冲突的请求直接返回 “手慢了”,既保证了并发量,又能控制超卖(即使偶尔有 1-2 个超卖,也能通过后续 “订单取消 + 库存回补” 解决,代价远低于悲观锁的性能损耗)。
3. 最终一致性即可(无需实时强一致)
典型场景:社交点赞、评论计数、用户行为统计
比如公众号文章 “10086 个点赞” 的计数:
- 用户点赞时,不需要实时保证 “每一次点赞都精确到个位数”—— 哪怕偶尔有两个点赞请求冲突,最终统计差 1-2 个,用户也感知不到;
- 用乐观锁:点赞时携带当前点赞数(比如前端传current_like=100),后端判断 “数据库点赞数 == 100” 就加 1,否则重试 1-2 次,失败就放弃(反正下次刷新会显示正确数),既不影响性能,又能满足业务需求。
四、一张表帮你快速决策:悲观锁 vs 乐观锁
| 对比维度 | 悲观锁 | 乐观锁 |
|---|---|---|
| 核心适用场景 | 金融转账、长事务、高冲突选座 | 秒杀抢购、读多写少、最终一致场景 |
| 并发量 | 低 - 中(高并发会阻塞) | 高(无阻塞,支持大量请求) |
| 一致性要求 | 强一致性(数据绝对不能错) | 最终一致性(允许短暂不一致) |
| 业务代价 | 性能损耗(等锁超时) | 冲突重试(需处理失败逻辑) |
| 实现方式 | 数据库行锁(select ... for update)、分布式锁(Redis/ZooKeeper) | 版本号(version)、时间戳、CAS 算法 |
五、业务决策小技巧:3 步选对锁
- 先看 “读写比例” :读多写少→乐观锁,写多读少→悲观锁;
- 再看 “一致性代价” :数据错误会导致资损(如金融)→悲观锁,偶尔错误可接受(如点赞)→乐观锁;
- 最后看 “并发量” :并发量超 1000 / 秒→优先乐观锁,并发量低→悲观锁更简单(不用处理重试)。
总结
悲观锁和乐观锁没有 “谁更好”,只有 “谁更适合”。记住:业务需求决定技术选择—— 当 “安全” 比 “快” 重要时,选悲观锁;当 “快” 比 “绝对安全” 重要时,选乐观锁。实际开发中,也可以结合两者(比如秒杀时用乐观锁预扣,支付时用悲观锁确认),灵活应对复杂业务场景。