乐观锁一般怎么实现

53次阅读
没有评论

 

在并发编程和数据库设计中,乐观锁(Optimistic Locking) 的核心思想是:假设冲突不会发生。它在读取数据时不加锁,只有在最后“提交更新”的一瞬间,才会检查这段时间内数据是否被别人改过。

如果数据没变,则更新成功;如果数据变了,则更新失败(通常报错或重试)。

常见的实现方式有以下三种:


1. 版本号机制(最常用)

这是数据库实现中最标准的方法。在表中新增一个字段 version

  • 步骤

    1. 读取记录,获取当前 version(假设为 1)。

    2. 在内存中修改业务数据。

    3. 更新时,执行带有条件的 SQL:

      UPDATE table SET score = 100, version = version + 1 WHERE id = 1 AND version = 1;

    4. 判断结果:如果影响行数为 1,表示成功;如果为 0,表示这期间有人改了数据,版本号已不再是 1,此时触发重试逻辑。


2. CAS (Compare And Swap) 机制

这是 Java 并发包(java.util.concurrent.atomic)底层的核心原理,依赖于 CPU 的硬件指令。

  • 原理:包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。

  • 逻辑:如果内存位置 V 的值等于预期原值 A,则将 V 修改为 B。否则,什么都不做。

  • Java 示例

    Java

    AtomicInteger count = new AtomicInteger(0);
    count.incrementAndGet(); // 内部通过 CAS 自旋实现
    
  • 缺点:ABA 问题。如果一个值从 A 变成 B 又变回 A,CAS 会认为它没变。

    • 解决办法:加时间戳或版本号(如 Java 中的 AtomicStampedReference)。


3. 时间戳(Timestamp)

逻辑与版本号类似,只是把 version 换成了 update_time

  • 逻辑:提交更新时比较最后修改时间是否一致。

  • 缺点:在高并发下,如果系统时钟精度不够(比如两个请求在同一毫秒内到达),可能会出现误判,安全性略低于递增的版本号。


乐观锁 vs 悲观锁:怎么选?

维度 乐观锁 悲观锁 (SELECT … FOR UPDATE)
核心假设 冲突极少发生。 冲突经常发生。
开销 低(不涉及内核态锁竞争)。 高(会造成线程阻塞、上下文切换)。
吞吐量 高。 低。
适用场景 读多写少(如商城修改个人信息)。 写多读少(如秒杀抢购、库存强一致性)。

结合你的业务:KeyGeneratorService

如果你在生成比特币私钥后需要把“发放记录”存入数据库(防止同一个私钥发给两个人),建议使用版本号乐观锁

SQL

-- 伪代码
UPDATE private_keys 
SET status = 'USED', user_id = 9527, version = version + 1 
WHERE key_id = 100 AND version = 5;

这样做可以保证在高并发发放私钥时,数据库不会因为大量的排他锁(Pessimistic Lock)而卡死。

既然聊到了并发控制,你想了解一下在高并发场景下,如何通过“重试策略(Retry Policy)”来处理乐观锁更新失败的情况吗?

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-03-18发表,共计1234字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)