悲观锁和乐观锁

悲观锁

核心思想

假设并发冲突一定会发生,因此在访问共享资源的时候,先加锁,确保操作期间数据不会被其它线程修改。

特点

  • 提前枷锁: 操作前获取锁,操作后释放锁
  • 阻塞等待: 如果锁被占用,其它线程会阻塞或者自旋等待,直到锁释放
  • 强一致性: 保证操作期间数据的独占性,适合写多少读多少的场景

典型实现

互斥锁:go的sync.Mutex

数据库行锁: 如SELECT … FOR UPDATE

使用场景

  • 写操作频繁,冲突概率高
  • 需要严格保证数据一致性。

乐观锁

核心思想

假设冲突很少发生,因此直接操作数据,仅在提交时检测是否发生冲突。如果冲突则重试或者放弃。

特点

  • 无锁操作: 操作期间不加锁,仅在提交时验证数据版本
  • 非阻塞: 线程不会互相阻塞,适合 读多写少的场景
  • 依赖版本机制: 通过版本号或者时间戳检测数据变更

典型实现

Go的atomic.CompareAndSwap

数据库版本控制,通过version字段实现。

适用场景

  • 读操作远远多于写操作
  • 冲突概率低,而且重试成本可控

对比总结

特性 悲观锁 乐观锁
冲突假设 认为一定会发生冲突 认为冲突很少发生
锁机制 操作前加锁 无锁,提交时检测冲突
性能 高并发写时性能差 高并发读时性能高
数据一致性 强一致性 最终一致性
实现复杂度 简单 需要处理冲突(如重试逻辑)
典型应用 数据库行锁、转账操作 计数器、库存系统、无锁数据结构

image-20250129162404265