真实面经题目 · 原创解析
数据库中乐观锁和悲观锁的应用场景是什么?
乐观锁适合读多写少、冲突概率低、业务能接受失败重试的场景;悲观锁适合写冲突高、强一致要求高、不能接受并发覆盖或超卖的场景。回答要围绕版本号/CAS、update where version、重试策略、select for update、行锁、事务边界、死锁风险、隔离级别和幂等性展开。
真实面经题目 · 原创解析
乐观锁适合读多写少、冲突概率低、业务能接受失败重试的场景;悲观锁适合写冲突高、强一致要求高、不能接受并发覆盖或超卖的场景。回答要围绕版本号/CAS、update where version、重试策略、select for update、行锁、事务边界、死锁风险、隔离级别和幂等性展开。
数据库里的乐观锁和悲观锁,本质区别是对并发冲突的假设不同。乐观锁假设冲突少,不提前加锁,而是在更新时通过 version、时间戳或状态条件做 CAS 校验,例如更新库存时带上 where id = ? and version = ? and stock > 0,如果影响行数为 0,说明数据已被别人改过,需要重试、返回失败或重新读取后再决策。它适合读多写少、低竞争、业务可重试的场景。悲观锁假设冲突多,会在事务中先锁住数据,比如 select ... for update,依赖数据库行锁保证同一时刻只有一个事务能修改这行。它适合写多冲突高、不能接受失败重试或必须串行化的场景,比如热点库存、余额扣减、支付状态变更。实际选型还要看事务长度、索引是否命中、隔离级别、死锁风险和幂等设计。
乐观锁不是数据库物理锁,而是一种基于版本号、时间戳、状态机或条件更新的并发控制思想。它通常在表中增加 version 字段,读取时拿到旧版本,更新时带上旧版本作为条件,只有版本仍然匹配才更新成功。悲观锁则依赖数据库锁机制,在事务内先锁定目标行,直到事务提交或回滚后释放锁。
乐观锁适合读多写少、冲突概率低、事务逻辑较短、业务允许失败重试的场景。例如用户修改个人信息、订单从待支付变为已支付、优惠券领取量不高时的库存扣减、后台配置更新等。它的优势是不会长时间阻塞其他事务,吞吐量较好;缺点是在热点竞争下大量更新会失败。
悲观锁适合写冲突高、数据价值高、业务不能接受并发覆盖的场景。例如账户余额扣减、热点商品库存、支付回调处理同一订单、同一资源的排他性分配。使用悲观锁时,常见模式是在事务中 select for update 查出目标行并锁住,然后完成校验与更新,最后提交事务。
两种锁主要都是为了解决丢失更新问题。典型丢失更新是两个事务同时读到库存为 10,各自扣 1,最后都写回 9,实际只扣了一次。乐观锁用 update where version 让第二个事务更新失败;悲观锁用行锁让第二个事务必须等第一个事务提交后再读取和修改。
低并发库存可以用乐观锁或条件更新,同时校验 stock > 0 和 version,失败后有限重试或直接提示库存不足。秒杀、热点库存、余额这类高冲突场景,如果全部依赖乐观锁,失败重试可能把数据库打满,通常要结合悲观锁、队列削峰、预扣库存、分段库存或 Redis 原子扣减。
悲观锁必须放在事务里才有意义,select for update 锁的范围和索引、隔离级别、执行计划有关。InnoDB 下如果 where 条件命中唯一索引,通常锁目标行;如果没有合适索引,可能扫描更多记录并扩大锁范围。乐观锁虽然不依赖长事务持锁,但也要保证版本校验完整。
乐观锁失败后不能无限重试,应设置最大次数、退避策略和明确失败结果。重试前要重新读取最新数据,并重新执行业务判断。支付回调、消息消费、下单扣库存等场景还必须设计幂等键、唯一约束或状态机条件,避免重复请求导致重复扣款、重复发货或重复扣库存。
悲观锁的主要风险是锁等待和死锁。常见死锁来自多个事务以不同顺序锁多行。治理方式包括固定加锁顺序、缩短事务时间、避免事务中远程调用、确保查询走索引、设置合理锁等待超时、捕获死锁异常后按幂等规则重试。
先根据业务决定是提示失败还是重试。可以重试的场景要重新读取最新数据,重新判断业务条件,再提交更新;同时限制重试次数并加入退避。
不一定。InnoDB 是否只锁目标行取决于索引、查询条件和隔离级别。如果没有合适索引,数据库可能扫描更多记录,导致锁范围扩大。
可以,但前提是更新条件完整,例如同时校验 version 和 stock > 0,并以影响行数判断是否扣减成功。高并发热点商品还需要限流、队列或缓存原子扣减。
因为多个事务可能以不同顺序持有并等待对方的锁。解决方法是统一加锁顺序、减少事务内操作、避免事务中调用外部服务,并对死锁异常做幂等重试。
需要。隔离级别解决事务读一致性,不等于自动满足所有业务并发约束。库存不能为负、订单状态单向流转、余额不能重复扣减,仍需要条件更新、版本号或 select for update。