悲观并发控制和乐观并发控制

悲观并发控制和乐观并发控制

悲观并发控制(Pessimistic Concurrency Control)和乐观并发控制(Optimistic Concurrency Control)的区别、原理和应用场景:

悲观并发控制

image

image


flowchart TD A([开始事务]) --> B[尝试获取锁] B --> C{锁是否可用?} C -->|是| D[加锁(排他锁/共享锁)] C -->|否| E[等待锁释放或超时] E --> B D --> F[操作数据(读/写)] F --> G[提交事务] G --> H[释放锁] H --> I([事务结束]) E -->|超时| J[事务回滚] J --> I


flowchart TD subgraph 事务A A1([事务A开始]) --> A2[获取锁1] A2 --> A3[操作数据1] A3 --> A4[尝试获取锁2] A4 --> A5{锁2是否可用?} A5 -->|否| A6[等待锁2释放] A5 -->|是| A7[完成操作并提交] end subgraph 事务B B1([事务B开始]) --> B2[获取锁2] B2 --> B3[操作数据2] B3 --> B4[尝试获取锁1] B4 --> B5{锁1是否可用?} B5 -->|否| B6[等待锁1释放] B5 -->|是| B7[完成操作并提交] end A6 -->|等待事务B释放锁2| B6 B6 -->|等待事务A释放锁1| A6 A6 -.->|超时或检测死锁| A8([事务A回滚]) B6 -.->|超时或检测死锁| B8([事务B回滚])

image

flowchart TD A([开始事务]) --> B[读取数据及当前版本号] B --> C[业务处理(修改数据)] C --> D{尝试提交} D --> E[检查版本号是否一致] E -->|版本一致| F[提交事务并更新版本号] E -->|版本不一致| G[回滚事务] G --> H{重试次数 < 最大重试次数?} H -->|是| B H -->|否| I[返回失败] F --> J([事务成功结束]) I --> K([事务失败结束])

1. 悲观并发控制

核心思想
假设多个事务之间容易发生冲突,因此在操作数据前先加锁,阻止其他事务访问,直到当前事务完成。

实现方式
  • 共享锁(读锁) :允许其他事务读取,但不能修改。
  • 排他锁(写锁) :禁止其他事务读写。
  • 锁的粒度:行锁、表锁、页锁等(例如 MySQL 的 SELECT ... FOR UPDATE​)。
典型场景
  • 高冲突环境(如银行转账、库存扣减)。
  • 需要强一致性的场景(如金融系统)。
优点
  • 保证数据一致性,避免脏读、不可重复读等问题。
  • 适合冲突频繁的场景。
缺点
  • 锁机制可能引发性能瓶颈(锁竞争、死锁)。
  • 事务响应时间较长(等待锁释放)。

2. 乐观并发控制

核心思想
假设事务之间冲突概率低,允许事务直接操作数据,仅在提交时检测冲突。如果冲突发生,则回滚并重试。

实现方式
  • 版本号(Versioning) :为数据记录添加版本号或时间戳。

  • 提交时校验:比较提交时的版本号与最初读取的版本号是否一致。

    • 一致:提交成功,更新版本号。
    • 不一致:回滚事务,提示重试。
典型场景
  • 低冲突环境(如论坛评论、用户资料编辑)。
  • 读多写少、响应速度要求高的场景(如 Web 应用)。
优点
  • 无锁设计,减少资源竞争,性能更高。
  • 适合高并发读操作。
缺点
  • 高冲突时频繁回滚和重试,性能下降明显。
  • 需要额外的冲突处理逻辑(如重试机制)。

3. 对比总结

维度 悲观并发控制 乐观并发控制
冲突假设 冲突频繁 冲突较少
锁机制 提前加锁 无锁,提交时校验
性能开销 锁管理、死锁检测 冲突检测、重试机制
适用场景 强一致性、高冲突(如转账) 高并发读、低冲突(如评论系统)
典型数据库 MySQL(行锁)、SQL Server MongoDB(默认乐观锁)、Redis

4. 如何选择?

  • 冲突频率:高冲突选悲观,低冲突选乐观。
  • 响应需求:要求低延迟选乐观,强一致性选悲观。
  • 重试成本:若重试代价高(如复杂业务逻辑),悲观更可靠。
  • 混合使用:部分场景可结合两种策略(例如分段锁+版本号)。