栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis秒杀demo

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis秒杀demo

方案一:方法上加上synchronized即可,线程安全方案,但是执行效率较低,不适合大型并发场合

方案二:采用乐观锁 

controller:

@RestController
public class SecondKill {

    @Autowired
    SecondKillSev secondKillSev;

    @RequestMapping(value = "/buy",method = RequestMethod.POST)
    public Mes buy(@RequestParam("proid") String proid){
        String usedId=new Random().nextInt(1000)+"";
        Mes mes = secondKillSev.doSecondKill(usedId, proid);
        return mes;
    }
}

service:

@Service
public class SecondKillSev {

    Mes message;

    public SecondKillSev(){

    }

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    
    public Mes doSecondKill(String uid, String proid){

        //1.判断uid和proid非空
        if(uid==null || proid==null){
            return Mes.getMes("400","uid和proid不能为空");
        }

        //2.连接redis
        ValueOperations ops = stringRedisTemplate.opsForValue();

        //3.拼接key,库存key,成功用户key
        String kcKey="kcKey:"+proid;
        String userKey="userKey:"+proid;

        //乐观锁解决超卖问题
        //stringRedisTemplate默认不开启事物
        stringRedisTemplate.setEnableTransactionSupport(true);

        List txResults = stringRedisTemplate.execute(new SessionCallback>() {
            public List execute(RedisOperations operations) throws DataAccessException {
                //监视库存
                operations.watch(kcKey);
                //4.获取库存,如果库存是null,则秒杀还没开始
                String kc=ops.get(kcKey);
                if(kc==null){
                    message= Mes.getMes("400","库存是null,秒杀还没开始");
                    return null;
                }

                //5.判断用户是否重复秒杀
                if(operations.opsForSet().isMember(userKey, uid)){
                    message=Mes.getMes("400","不能重复秒杀");
                    return null;
                }

                //6.判断商品数量,如果数量小于1,则秒杀结束
                if(Integer.parseInt(kc)<=0){
                    message=Mes.getMes("400","秒杀已经结束");
                    return null;
                }

                //7.秒杀过程,库存-1,成功用户添加到清单
                //使用事务
                operations.multi();
                operations.opsForValue().decrement(kcKey);
                operations.opsForSet().add(userKey,uid);

                List exec = operations.exec();
                if(exec==null || exec.size()==0){
                    message=Mes.getMes("400","秒杀失败");
                    return null;
                }else{
                    return exec;
                }
            }
        });

        if(txResults==null ||txResults.size()==0){
            return message;
        }

        return Mes.getMes("200","秒杀成功");
    }
}
 

但是这种方案也有一个缺点,就是如果商品的数量是500,但是线程数是1000,即访问量不是非常多的情况下,会出现库存没有卖完的问题,即库存遗留问题,所以可以用lua脚本来解决;

方案三:lua脚本

local userid=KEYS[1];
local prodid=KEYS[2];
local qtkey="sk:"..prodid..":qt";
local userskey="sk:"..prodid..":user";
local userExists=redis.call("sismember",userskey,userid);
if tonumber(userExists)==1 then
  return 2;
end
local num=redis.call("get",qtkey);
if tonumber(num)<=0 then
  return 0;
else
  redis.call("decr",qtkey);
  redis.call("sadd",userskey,userid);
  end
  return 1

代码实现:

@Service
public class SeckillByScript {
    static String secKillScript1 = "local userid=KEYS[1];n" +
            "local prodid=KEYS[2];n" +
            "local qtkey="sk:"..prodid..":qt";n" +
            "local userskey="sk:"..prodid..":user";n" +
            "local userExists=redis.call("sismember",userskey,userid);" +
            "if tonumber(userExists)==1 thenn" +
            "  return 2;n" +
            "endn" +
            "local num=redis.call("get",qtkey);n" +
            "if tonumber(num)<=0 thenn" +
            "  return 0;n" +
            "elsen" +
            "  redis.call("decr",qtkey);n" +
            "  redis.call("sadd",userskey,userid);n" +
            "  endn" +
            "  return 1";

    public boolean doSecKill(String userid, String prodid) {
        JedisPool jedisPool = JedisPoolUtil.getJedisPool();
        Jedis jedis = jedisPool.getResource();
        String sha1 = jedis.scriptLoad(secKillScript1);
        Object result = jedis.evalsha(sha1, 2, userid, prodid);
        String reString = String.valueOf(result);
        if ("0".equals(reString)) {
            System.out.println("已抢空!");
            return false;
        } else if ("1".equals(reString)) {
            System.out.println("抢购成功");
            return true;
        } else if ("2".equals(reString)) {
            System.out.println("该用户已抢过!");
            return false;
        } else {
            System.out.println("抢购异常");
            return false;
        }
    }
}

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号