【SSM抢红包简单项目】 ---- (三) 悲观锁和乐观锁消除超发问题

本文介绍了在SSM抢红包项目中如何利用悲观锁和乐观锁来解决超发问题。悲观锁通过`for update`避免并发更新,但可能导致性能下降;乐观锁采用CAS原理,通过版本号解决ABA问题,但在高并发下可能存在请求失败。此外,文中还探讨了乐观锁的重入机制,包括按时间戳和按次数的重入策略,以提高请求成功率。
摘要由CSDN通过智能技术生成

目录

1. 悲观锁
2. 乐观锁
3. 乐观锁重入机制的实现

超发问题分析
针对上篇文章中,用户抢到红包后,红包总量应-1,当多个用户同时抢红包,此时多个线程同时读得库存为n,相应的逻辑执行后,最后将均执update T_RED_PACKET set stock = stock - 1 where id = #{id} ,很明显这是错误的。

1.悲观锁(for update)

悲观锁是一种利用数据库内部机制提供的锁的方法,对更新的数据加锁, 在并发期间一旦有一个事务持有了数据库记录的锁,其他的线程将不再对数据进行更新了。

1.1 项目使用悲观锁操作

  • 线程1查询红包数时使用排他锁
    <!--查询红包具体信息-->
    <select id="getRedPacket" parameterType="long" resultType="cn.whc.pojo.RedPacket">
        select id, user_id as userId, amount, send_date as sendDate, total, unit_amount as unitAmount, stock,
        version, note from T_RED_PACKET where id = #{id} for update
    </select>
    
  • 然后进行后续操作(redPacketDao.decreaseRedPacket和userRedPacketDao.grapRedPacket)更新红包数量,最后提交事务
  • 线程2在查询红包数时,如果线程1还未释放排他锁,线程2将等待
  • 线程3同线程2,以此类推

1.2 共享锁(S锁) 和 排他锁(X锁)
共享锁和排他锁是悲观锁不同的实现,都属于悲观锁的范畴

数据库的增删改操作默认都会加排他锁,但查询不会加任何锁

共享锁: 指的是对于多个不同的事务,对同一资源共享同一个锁。对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源,但无法修改。
排他锁: 指对于多个不同的事务,对同一资源只能有一把锁。对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。

1.3 悲观锁消除超发问题

在RedPacket.xml中查询红包具体信息后面添加for update语句,为查询增加行级锁,这样就不会出现超发现象引发数据不一致性问题了

<!--查询红包具体信息-->
    <select id="getRedPacketForUpdate" parameterType="long" resultType="cn.whc.pojo.RedPacket">
        select id, user_id as userId, amount, send_date as sendDate, total, unit_amount as unitAmount, stock,
        version, note from T_RED_PACKET where id = #{id} for update
    </select>

测试结果:
在这里插入图片描述
性能问题:
在这里插入图片描述
目前只对数据库加了一个锁,显示结果还不太明显,但是如果加得锁比较多的时候,数据库的性能会持续下降,原因如下:

  • 对于悲观锁来说,当一条线程抢占了资源后,其他的线程将得不到资源,这个时候,CPU就会将这些得不到资源的线程挂起,挂起的线程也会消耗CPU的资源,尤其是在高并发的请求中

2.乐观锁

乐观锁是一种不会阻塞其他线程并发的机制,它不会使用数据库的锁进行实现。
乐观锁使用的是CAS原理。

2.1 CAS原理
在CAS原理中,对于多个线程共同的资源,先保存一个旧值,比如进入线程后,查询当前存量为100个红包,那么先把旧值保存为100,然后经过一定的逻辑处理。当需要扣减红包的时候,先比较数据库当前的值和旧值是否一致,如果一致则进行扣减红包的操作,否则就认为它已经被其他线程修改过,不再进行操作

CAS可能会存在ABA问题,只要在数据中加入一个版本号的属性,就能解决ABA问题

2.2 乐观锁实现抢红包业务
规避CAS原理产生的ABA问题,需要先在红包表(T_RED_PACKET)加入一个新的列版本号(version),我们已经在一开始创建表的时候就建立了

<!--通过版本号扣减抢红包,每更新一次,版本增1,其次增加对版本号的判断-->
    <update id="decreaseRedPacketForVersion">
        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值