MySQL 的乐观锁和悲观锁是什么?

在 MySQL 中,乐观锁悲观锁是两种不同的并发控制机制,用于解决多用户/事务同时操作数据时的冲突问题。它们的核心理念和实现方式有显著区别:


1. 悲观锁(Pessimistic Locking)

核心思想:

假设数据会被频繁修改,因此提前对数据进行加锁,防止其他事务访问,直到当前事务完成操作并释放锁。

实现方式:
  • 显式加锁:通过 SQL 语句主动申请锁。
    • 排他锁(X Lock)SELECT ... FOR UPDATE
      (在事务中锁定选中的行,阻止其他事务修改或加锁)
    • 共享锁(S Lock)SELECT ... LOCK IN SHARE MODE
      (允许其他事务读,但阻止写操作)
  • 隐式加锁:数据库自动管理(如 MySQL 的 InnoDB 引擎默认使用行级锁)。
适用场景:
  • 写多读少的场景(如频繁更新的数据)。
  • 需要强一致性且冲突概率较高时(如金融交易)。
示例:
START TRANSACTION;
-- 锁定用户账户余额(排他锁)
SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;
-- 执行扣款操作
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
COMMIT;
优点:
  • 保证数据操作的原子性和一致性。
  • 适合高竞争场景(冲突概率高时效率更高)。
缺点:
  • 可能导致死锁(需应用层处理)。
  • 降低并发性能(长时间持有锁会阻塞其他操作)。

2. 乐观锁(Optimistic Locking)

核心思想:

假设数据冲突较少发生,因此不加锁,而是在更新时检查数据是否被修改过。若被修改过,则拒绝操作或重试。

实现方式:
  • 版本号(Version):在表中增加 version 字段,更新时验证版本号。
    UPDATE table 
    SET 
      column = new_value, 
      version = version + 1 
    WHERE 
      id = 1 AND version = old_version;
    
  • 时间戳(Timestamp):类似版本号,但使用时间戳标记数据修改时间。
  • CAS(Compare-And-Swap):在应用层比较数据一致性后再提交。
适用场景:
  • 读多写少的场景(如商品库存充足时的秒杀)。
  • 冲突概率较低时(如用户点赞操作)。
示例:
-- 初始查询(获取当前版本号)
SELECT balance, version FROM accounts WHERE user_id = 1;

-- 更新时检查版本号
UPDATE accounts 
SET 
  balance = balance - 100, 
  version = version + 1 
WHERE 
  user_id = 1 AND version = 1; -- 假设旧版本号是1

-- 如果受影响行数=0,说明版本已过期,需重试或报错
优点:
  • 无锁竞争,并发性能高。
  • 避免死锁问题。
缺点:
  • 冲突发生时需处理重试逻辑(如循环重试或返回错误)。
  • 无法保证强一致性(最终一致性)。

3. 对比总结

特性 悲观锁 乐观锁
加锁时机 操作前加锁 操作后验证
实现复杂度 依赖数据库机制 需应用层配合(如版本号)
并发性能 较低(锁竞争) 较高(无锁)
适用场景 高竞争、强一致性 低竞争、最终一致性
典型问题 死锁、性能瓶颈 版本冲突、重试逻辑

4. MySQL 中的实际选择

  • InnoDB 引擎:支持行级锁,适合悲观锁(需显式使用 FOR UPDATE)。
  • MyISAM 引擎:仅支持表级锁,悲观锁性能较差,通常不推荐。
  • 乐观锁:需在应用层实现(如通过版本号字段),与存储引擎无关。

根据业务场景选择:

  • 金融交易等强一致性场景 → 悲观锁
  • 高并发读多写少场景 → 乐观锁

我正在编程导航学习项目课程,和其他编程爱好者一起交流进步,你也一起来吧
点击进入

你可能感兴趣的:(春招热门面试题,mysql,数据库)