1 悲观锁

<!-- 查询红包具体信息 -->
 <select id="getRedPacketForUpdate" parameterType="int"
     resultType="test814RedPacket.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>

  for update

在sql后面加 for update ,

注意,在 SQL 中加入的 for update 语句,意味着将持有对数据库记录的行更新锁(因 为这里使用主键查询,所以只会对行加锁。如果使用的是非主键查询,要考虑是否对全表 加锁的问题,加锁后可能引发其他查询的阻塞〉,那就意味着在高并发的场景下 条事 务持有了这个更新锁才能往下操作,其他的线程如果要更新这条记录,都需要等待,这样 就不会出现超发现象引发的数据一致

发现一个好玩的问题:

在UserRedPacketServiceimpl 中,如果你注释掉

redisson redis 对应版本_高并发

 就是不要事务,还是会超发

redisson redis 对应版本_高并发_02

 加上之后才不会。有趣

2乐观锁

说吧了就是判断下之前的stock值有没变化,有变化则说明数据改变,不操作。但是可能存在bab问题,所以加个版本,每次数据改变,版本+1,在对数据修改之前,判断版本,没有变化才操作数据。

<!-- 通过版本号扣减抢红包 ,每更新一次,版本加1,其次增加对版本的判断  注意 的代 减红包的时 增加了对版本号的判断,其次每次扣减都会对
版本号 ,这样保证每次更新在版本号上有记录 从而避免 ABA 问题。对于查询也不使
for update 语旬 避免锁 发生 这样就没有线程阻塞的 题了-->

<update id="decreaseRedPacketForVersion" >
  update T_RED_PACKET set stock = stock -1,version=version+1 where id =#{id} and version = #{version}
 </update>

 这里是主要改动,其余都是改动调用,看下结果:

redisson redis 对应版本_高并发_03

我设置了2500左右的人抢2000个红包,看到没,剩下不少.....

所以再优化,重入机制

也就是 旦因为版本原因 没有抢到红包,则重新尝试抢红包,但是过多的重入会造成大量的 SQL 执行,所以目前流 行的重入会加入两种限制, 1种是按时间戳的重入,也就是在 定时间戳内不成功的会循环到成功为止,直至超过时间戳,不成功才会退出,返回失败。另外 1种是按次数,比如限定 3次,程序尝试超过3 次抢红包后,就判定请求失效,这样有助 于提高用户抢红包的成功率,下面讨论如何重入。

redisson redis 对应版本_redis_04

加个时间而已,你也可以把时间换成次数

for (int i = 0; i < 3; i++) {
             //获取红包信息,
             RedPacket redPacket = redPacketMapper.getRedPacket(redPacketId);        
             //当前红包数大于0
             if(redPacket.getStock()>0){
                 //再次传入线程保存的 version 旧值给 SQL 判断,是否有其他线程修改过数据            
                 int  update = redPacketMapper.decreaseRedPacketForVersion(redPacketId,redPacket.getVersion());
                 //如果没有数据更新,说明其他线程已经更新过数据,本次抢红包失败
                 if(update==0){
                     return FAILED;
                 }

 

3 redis

对于使用 redis 实现抢红包 首先需要知道的是redis的功能不如数据库强大,事务也 不完整,因此要保证数据的正确性,数据的正确性可以通过严格的验证得以保证。而  Lua 语言是原子性的,且功能更为强大 所以优先选择使用 Lua 语言来实现抢红包