乐观锁,悲观锁

乐观锁悲观锁是两种常见的并发控制机制,主要用于解决并发操作中的数据一致性问题。它们的应用场景和实现方式各有特点:


1. 乐观锁

定义

  • 乐观锁基于乐观的并发控制思想,假设事务间的冲突概率较低,因此不对资源加锁。
  • 在更新数据时,通过某种机制(如版本号或时间戳)来检测是否发生了冲突,如果检测到冲突,事务会回滚或重试。

实现方式

  1. 在表中增加一个版本号字段version)或时间戳字段
  2. 读取数据时一并读取版本号。
  3. 更新时将版本号作为条件,如果版本号没有变化,则允许更新;否则,更新失败。

SQL 示例

-- 假设有一个版本号字段 version

-- 事务 A 读取数据
SELECT balance, version FROM accounts WHERE id = 1;

-- 事务 A 更新数据,条件是版本号未被其他事务修改
UPDATE accounts
SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = 1;

-- 如果条件未满足(版本号不匹配),更新失败,需要重试。

特性

  • 优点
    • 无需加锁,对读取操作没有阻塞,性能较高。
    • 适合读多写少的场景。
  • 缺点
    • 如果发生冲突,可能需要多次重试,增加了应用程序的复杂度。

2. 悲观锁

定义

  • 悲观锁基于悲观的并发控制思想,假设事务间的冲突概率较高,因此对资源加锁以避免冲突。
  • 在读取数据时直接加锁,确保其他事务无法修改或读取该数据。

实现方式

  • 通过数据库提供的锁机制(如共享锁(S 锁)排他锁(X 锁))来实现。
  • 常用语法:
    • 共享锁LOCK IN SHARE MODE
    • 排他锁FOR UPDATE

SQL 示例

-- 悲观锁使用排他锁,防止其他事务修改该行数据
START TRANSACTION;

-- 读取数据并加排他锁
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;

-- 更新数据
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

COMMIT;

特性

  • 优点
    • 通过锁机制保证数据一致性,不需要额外的版本检测。
    • 适合写多读少高并发更新的场景。
  • 缺点
    • 会阻塞其他事务的操作,可能导致性能降低。
    • 容易发生死锁,需要特别注意加锁顺序。

3. 乐观锁与悲观锁的对比

特性 乐观锁 悲观锁
加锁机制 不加锁,通过版本号或时间戳控制并发 通过数据库锁机制加锁
适用场景 读多写少,冲突概率低 写多读少,冲突概率高
并发性能 性能较高,无锁操作 性能较低,可能阻塞其他事务
冲突处理 冲突时需要回滚或重试 无冲突,直接完成事务
实现复杂度 需要应用逻辑支持(版本号或时间戳) 数据库原生支持
事务阻塞 不会阻塞其他事务 可能阻塞其他事务

4. 如何选择

  1. 乐观锁适合以下场景:

    • 读操作频繁、写操作较少。
    • 数据冲突概率较低。
    • 对系统性能要求较高。
  2. 悲观锁适合以下场景:

    • 写操作频繁。
    • 冲突概率较高,需要严格的数据一致性。
    • 数据库支持较好,可以接受一定的阻塞。

5. 综合示例

假设一个账户余额更新系统,多个事务可能同时读取和更新账户余额:

乐观锁

事务读取时不加锁,更新时检测版本号是否变化。

-- 读取数据
SELECT balance, version FROM accounts WHERE id = 1;

-- 更新数据
UPDATE accounts
SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = 1;

悲观锁

事务读取时加锁,防止其他事务同时操作。

START TRANSACTION;
-- 读取并加锁
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;

-- 更新
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

COMMIT;

通过选择合适的锁机制,可以在性能和一致性之间找到平衡。

注意:该内容由由AIGC提供。

posted @ 2024-12-26 09:31  长空nice  阅读(6)  评论(0编辑  收藏  举报