乐观锁实现库存控制

一、什么是乐观锁


乐观锁是一种基于版本控制的并发控制机制。在乐观锁的思想中,认为数据访问冲突的概率很低,因此不加锁直接进行操作,但在更新数据时会进行版本比对,以确保数据的一致性。

乐观锁的原理主要基于版本号或时间戳来实现。在每次更新数据时,先获取当前数据的版本号或时间戳,然后在更新时比对版本号或时间戳是否一致,若一致则更新成功,否则表示数据已被其他线程修改,更新失败。

在Java中,常见的乐观锁实现是使用Atomic类,例如AtomicInteger、AtomicLong等。这些类提供了原子操作,可以确保对共享资源的更新操作是原子性的,从而避免了锁的开销和线程等待,另外,CAS(Compare-And-Swap)是实现乐观锁的核心算法,它通过比较内存中的值是否和预期的值相等来判断是否存在冲突。如果存在,则返回失败;如果不存在,则执行更新操作。Java中提供了AtomicInteger、AtomicLong、AtomicReference等原子类来支持CAS操作。

二、乐观锁适用于什么场景

写操作较少:在这种场景下,多个事务或线程大部分时间都在读取数据,而写操作的频率相对较低。乐观锁能够减少锁的持有时间,允许多个事务或线程同时读取数据,而不会相互阻塞。
数据冲突较少:如果数据更新操作之间的冲突较少,即多个事务或线程同时更新同一份数据的概率较低,那么乐观锁能够发挥很好的性能。因为即使偶尔出现冲突,也只是在更新数据时才会被检测到,而不需要在整个数据处理过程中都锁定资源。
重试成本较低:乐观锁在检测到冲突时会回滚事务或提示冲突,需要客户端重新尝试更新操作。因此,如果重试的成本较低(例如,重试不会导致大量计算或I/O操作),那么使用乐观锁是合适的。
系统能够容忍一定程度的失败:由于乐观锁在更新数据时可能会因为版本冲突而失败,因此系统需要能够处理这种失败情况。如果系统能够容忍一定程度的失败(例如,通过重试或其他补偿机制来恢复),那么使用乐观锁是可行的。

三、乐观锁优缺点


优点:
高并发高吞吐:乐观锁不会阻塞其他事务的读取操作,只在提交时检查数据是否被修改,因此可以提供更好的并发性能。
无锁操作:乐观锁不需要显式地获取和释放锁,减少了锁竞争和上下文切换的开销。
无死锁风险:由于乐观锁不会阻塞其他事务的访问,因此不会出现死锁的情况。
缺点:
冲突处理复杂:由于乐观锁不会阻塞其他事务,因此在提交时需要检查数据是否被其他事务修改,如果发现冲突,需要回滚事务或重新尝试操作,这增加了冲突处理的复杂性。
数据一致性风险:乐观锁假设并发冲突较少,因此可能存在数据一致性的风险。如果多个事务同时对同一数据进行修改,可能会导致数据不一致的情况。
需要额外字段:为了实现乐观锁,通常需要在数据表中添加额外的版本号或时间戳字段,这增加了存储空间的需求。
处理不当造成死循环风险:在大多数业务中乐观锁更新失败都会进行自旋,如果没有控制好自旋退出逻辑可能会造成递归死循环问题。

四、使用示例

简单业务代码:

    @Override
    public String test1() {
        SumDO sumDO = sumMapper.selectById(1);
        if (sumDO.getSum() <= 0) {
            log.info(sumDO.getSum() + ":库存不足");
            return "库存不足";
        }
        Boolean update = sumMapper.updateByVersion(sumDO.getId(), sumDO.getVersion(), sumDO.getVersion() + 1);
        if (update) {
            System.out.println(update + ":购买成功,剩余数量 = " + (sumDO.getSum() - 1));
        } else {
            System.out.println(update + ":当前人数过多,修改失败 = " + (sumDO.getSum()));
        }
        return update.toString();
    }

数据库修改语句

    @Update("UPDATE `sum` SET `sum` = sum  - 1 , version = #{newVersion} WHERE `id` = #{id} and version = #{version}")
    Boolean updateByVersion(int id, int version,int newVersion);

五、验证结果

初始数据库数据 5个库存,版本号为1

通过20个线程并发测试

结果五个全部卖出

 数据库库存为0,未产生超卖的情况

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL乐观锁是一种常见的并发控制机制,常用于处理库存相关的业务场景。在这种场景下,经常会有多个用户同时查询和修改库存数量,为了保证数据的准确性和一致性,可以采用乐观锁机制。 乐观锁的原理是,在进行数据更新操作前,先进行数据版本的判断。在库存表中,可以添加一个版本号字段,每次对库存进行修改时,版本号会进行更新。当用户进行库存修改操作时,先查询当前库存数量以及对应的版本号,然后进行修改时,判断查询结果的版本号是否和当前库存表中版本号一致。若一致,则可以执行修改操作,同时更新版本号;若不一致,则说明数据已经被其他用户修改,需要进行相应的处理,如重新查询库存数量或提示用户无法进行修改操作。 在实际实现中,可以使用乐观锁的方式来避免库存并发修改带来的问题。例如,用户A和用户B同时查询到库存为10,并开始执行库存减1的操作。当用户A执行更新操作时,会将版本号更新为1,并将库存减1,此时库存为9。而当用户B执行更新操作时,由于库存表中的版本号已变为1,而查询结果中的版本号仍未0,因此判断版本号不一致,这时用户B可以根据实际情况选择重新查询库存数量或进行其他操作。 乐观锁在处理库存并发修改时,能够有效地保障数据的准确性和一致性,避免了并发操作引起的问题。但需要注意的是,使用乐观锁机制时,要合理处理并发冲突,例如设置重试机制或采用其他的处理策略,以保证数据的正确性和业务的正常进行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值