高并发抢红包案列以及使用锁,版本号,redis缓存解决,项目可运行,详细注释(二)

1 悲观锁


  for update

在sql后面加 for update ,

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

发现一个好玩的问题:

在UserRedPacketServiceimpl 中,如果你注释掉

高并发抢红包案列以及使用锁,版本号,redis缓存解决,项目可运行,详细注释(二)_第1张图片

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

高并发抢红包案列以及使用锁,版本号,redis缓存解决,项目可运行,详细注释(二)_第2张图片

 加上之后才不会。有趣

2乐观锁

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


 update T_RED_PACKET set stock = stock -1,version=version+1 where id =#{id} and version = #{version}

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

高并发抢红包案列以及使用锁,版本号,redis缓存解决,项目可运行,详细注释(二)_第3张图片

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

所以再优化,重入机制

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

高并发抢红包案列以及使用锁,版本号,redis缓存解决,项目可运行,详细注释(二)_第4张图片

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

    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 语言来实现抢红包

 

下一篇文章介绍

 

 

 

你可能感兴趣的:(高并发)