← 每日面试题 学习看板

乐观锁与悲观锁如何实现?

面试题/03数据库中间件/01MySQL/05事务与锁机制/乐观锁与悲观锁如何实现?.md

面试记录 2026-09-17

题目:乐观锁与悲观锁如何实现?

难度:medium

标签:db/mysql/transaction, mysql/optimistic-lock, difficulty/medium

评分:⭐⭐⭐☆(3/5)

我的回答

MySQL 中乐观锁与悲观锁。乐观锁其实就是比较交换,先比较一下,比如设一个版本号,版本比较之前跟比较之后,如果是比较之前是一致的,然后就可以更新,如果版本号不一致就不更新。所谓乐观锁就是觉得它没什么问题,不用加锁,然后如果发生了变动才判定这个锁失效了,就不再执行。悲观锁的话,就是直接强加锁,for update,update 这种直接就是本身的 MySQL 里边它自己的锁,它本身的锁就是一个悲观锁的执行,并不是乐观锁。比如它的行锁、列锁、间隙锁,这些其实全都是悲观锁。如何实现呢?悲观锁它实现的基于场景其实是在索引上加锁,把当前的要处理的行上加一个锁,或者是对应的一个空间,一个锁的,在区间内加锁,区间锁,锁住这部分数据,在其他的执行时候,其他数据执行来过来执行的时候,如果是更新的写锁,其他来读死都不能读,然后就会导致对应的数据,让其他的 SQL 来执行的话,事务是不可以查到的。

点评

优点:

不足:

补充:

完整讲解

第一部分:知识重点剖析

核心考点定位:理解乐观锁与悲观锁的本质差异(冲突时机的假设不同),掌握 MySQL 中的具体实现方式与选型判断。

知识点拆解:

  1. 本质差异:
  1. MySQL 中的乐观锁实现:MySQL 本身没有乐观锁,需要在应用层做。标准做法是加 version 列:
   UPDATE t SET col=?, version=version+1 WHERE id=? AND version=?;

影响行数 = 1 表示成功;影响行数 = 0 表示版本冲突,业务层选择重试或失败。这也是 CAS(Compare-And-Swap) 的数据库版本。

  1. MySQL 中的悲观锁实现:
  1. 加锁粒度:
  1. MySQL 8.0 增强:
  1. 死锁与监控:
  1. 选型原则:

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. SELECT FOR UPDATE 在 RC 和 RR 隔离级别下锁的粒度有什么区别?
  1. FOR UPDATE SKIP LOCKED 可以用来做什么?
  1. 乐观锁的 version 列,遇到重试风暴怎么办?

第三部分:面试者参考回答(口语化)

面试官您好,乐观锁和悲观锁本质上是两种对"冲突概率"的假设。悲观锁假设"我一定会冲突",操作前先加锁,别人只能等;乐观锁假设"大概率不会冲突",先不加锁直接干活,提交前再校验,冲突才失败重试。

悲观锁在 MySQL 里是原生支持的。核心语法是 SELECT ... FOR UPDATE,它会给读到的行加排他锁,其他事务读、写都得等。8.0 之后还有 SELECT ... FOR SHARE,加的是共享锁,其他事务读可以并行,只有写要等。UPDATE 和 DELETE 本身就会隐式加 X 锁,不需要显式声明。加锁粒度主要有行锁、临键锁(next-key lock)、间隙锁,RR 隔离级别下默认用临键锁防止幻读。

8.0 还有两个增强语法我觉得很实用:FOR UPDATE NOWAIT,拿不到锁立即报错不阻塞;FOR UPDATE SKIP LOCKED,跳过已经被加锁的行,特别适合任务队列场景——多个 worker 抢任务,跳过的行让下一个 worker 处理,互不干扰。

乐观锁 MySQL 没有原生实现,要在应用层做。标准做法是表里加一个 version 列,更新的时候带上版本条件:UPDATE t SET col=?, version=version+1 WHERE id=? AND version=?。这条 SQL 的影响行数如果是 1,说明成功;如果是 0,说明别人在我读和写之间改过了,版本对不上,业务层决定是重试还是失败。这本质就是 CAS,Compare-And-Swap 的数据库版本。

选型上,悲观锁适合高冲突、强一致的场景,比如余额扣减、订单支付;乐观锁适合低冲突、能容忍重试的场景,比如点赞计数、普通库存扣减。悲观锁的代价是并发度低、有死锁风险、有超时问题,MySQL 默认 innodb_lock_wait_timeout=50s,超过就报错。死锁的话 innodb_deadlock_detect=ON(默认开启),InnoDB 会通过 wait-for graph 检测到死锁,回滚 undo log 少的那个事务。要排查线上死锁看 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 段,或者查 performance_schema.data_lock_waits 看锁等待链。

https://chat.deepseek.com/share/1f4jfjd7lcbtnwc0hu