跳至内容
2. 乐观锁与悲观锁

2. 乐观锁与悲观锁

Q: MySQL乐观锁和悲观锁是什么?

悲观锁(Pessimistic Lock)

基本思想

悲观锁认为被它保护的数据是极其不安全的,每时每刻都有可能被改动,一个事务拿到悲观锁后,其他任何事务都不能对该数据进行修改,只能等待锁被释放才可以执行。

实现方式

数据库中的行锁、表锁、读锁、写锁均为悲观锁。

-- 悲观锁示例:使用 SELECT ... FOR UPDATE
BEGIN;
SELECT * FROM account WHERE id = 1 FOR UPDATE;  -- 加排他锁
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;

乐观锁(Optimistic Lock)

基本思想

乐观锁认为数据的变动不会太频繁。

实现方式

乐观锁通常是通过在表中增加一个**版本(version)时间戳(timestamp)**来实现,其中,版本最为常用。

工作流程

  1. 事务在从数据库中取数据时,会将该数据的版本也取出来(v1)
  2. 当事务对数据变动完毕想要将其更新到表中时,会将之前取出的版本v1与数据中最新的版本v2相对比
  3. 如果v1=v2,那么说明在数据变动期间,没有其他事务对数据进行修改,此时,就允许事务对表中的数据进行修改,并且修改时version会加1,以此来表明数据已被变动
  4. 如果v1不等于v2,那么说明数据变动期间,数据被其他事务改动了,此时不允许数据更新到表中,一般的处理办法是通知用户让其重新操作
-- 乐观锁示例:使用版本号
-- 1. 查询时获取版本号
SELECT id, balance, version FROM account WHERE id = 1;
-- 假设查到:id=1, balance=1000, version=5

-- 2. 更新时检查版本号
UPDATE account 
SET balance = balance - 100, version = version + 1 
WHERE id = 1 AND version = 5;

-- 3. 检查影响行数
-- 如果影响行数为0,说明版本号已变化,更新失败,需要重试

Q: 乐观锁和悲观锁的适用场景?

悲观锁适用场景

  • 写操作频繁:数据冲突概率高的场景
  • 对数据一致性要求极高:不能容忍任何数据不一致
  • 短事务:锁持有时间短,不会长时间阻塞其他事务

乐观锁适用场景

  • 读操作频繁:数据冲突概率低的场景
  • 可以容忍重试:冲突时可以重新尝试操作
  • 长事务:避免长时间持有锁

性能对比

对比项悲观锁乐观锁
冲突处理阻塞等待重试机制
性能冲突频繁时性能好冲突少时性能好
实现复杂度简单(数据库支持)复杂(需要应用层实现)
死锁风险
适用场景写多读少读多写少
注意:不同于悲观锁,乐观锁通常是由开发者在应用层实现的,数据库本身不直接提供乐观锁机制。