redis秒杀案例,跟着b站尚硅谷老师学习

本文介绍了三种基于Redis解决秒杀场景中高并发问题的方法:1) 使用Redis事务,但存在库存遗留问题;2) 通过Lua脚本实现原子操作,确保库存准确减少并防止重复购买;3) 利用Redis锁,演示了如何在Java中使用RedisTemplate和Redission进行加锁和解锁操作,保证操作的顺序性。文章提供了具体的Java代码示例,展示了如何在Controller和Service层实现秒杀逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、使用redis的事务解决秒杀问题,但是会产生库存遗留问题

二、使用lua脚本解决秒杀问题

0、使用lua脚本的优势

在这里插入图片描述

1、具体使用的service层代码

package com.bear.service;

import com.bear.config.JedisPoolUtil;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.io.IOException;

/**
 * <简述>
 * <详细描述>
 *
 * @author LiuShanshan
 * @version $Id$
 */
@Service
public class SewckillServiceTwo {

    String secKillScript ="local userid=KEYS[1];\r\n" +
            "local prodid=KEYS[2];\r\n" +
            "local qtkey='sk:'..prodid..\":qt\";\r\n" +
            "local usersKey='sk:'..prodid..\":usr\";\r\n" +
            "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" +
            "if tonumber(userExists)==1 then \r\n" +
            "   return 2;\r\n" +
            "end\r\n" +
            "local num= redis.call(\"get\" ,qtkey);\r\n" +
            "if tonumber(num)<=0 then \r\n" +
            "   return 0;\r\n" +
            "else \r\n" +
            "   redis.call(\"decr\",qtkey);\r\n" +
            "   redis.call(\"sadd\",usersKey,userid);\r\n" +
            "end\r\n" +
            "return 1" ;


    public boolean doSecKill(String prodid,String uid) throws IOException {

        JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
        Jedis jedis=jedispool.getResource();

        //String sha1=  .secKillScript;
        String sha1=  jedis.scriptLoad(secKillScript);
        Object result= jedis.evalsha(sha1, 2, uid,prodid);

        String reString=String.valueOf(result);
        if ("0".equals( reString )  ) {
            System.err.println("已抢空!!");
        }else if("1".equals( reString )  )  {
            System.out.println("抢购成功!!!!");
        }else if("2".equals( reString )  )  {
            System.err.println("该用户已抢过!!");
        }else{
            System.err.println("抢购异常!!");
        }
        jedis.close();
        return true;
    }
}

2、controller层

    /**
     * 秒杀测试使用lua脚本
     * @return
     */
    @GetMapping("/seckill/test")
    public Boolean seckillTest() throws IOException {
        // 固定商品id ;// 自动生成的用户id
        return sewckillServiceTwo.doSecKill("1001", Long.toString(System.currentTimeMillis()));
    }


三、使用锁的方式解决秒杀问题(这里在java里面使用的方式是redisTemplate提供的操作redis锁的方式)

   /**
     *<简述> 正确的做法应该是让用户传入商品id  + 当前的用户id;下面我模拟多线程的操作过程,自己生成用户id(不对),会造成线程堵塞
     *<详细描述>
     * @author Liushanshan
     * @param pid
     * @param userid
     * @return void
    */
    @GetMapping("/testLock")
    public void testLock(@RequestParam("pid") String pid, @RequestParam("userid") String userid){
        //1获取锁,setne
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", userid, 5, TimeUnit.SECONDS);
        //2获取锁成功、查询num的值
        if(lock){
            // 商品名称
//            String pid = "pid";
            // 人
            String user =  Long.toString(System.currentTimeMillis());

            Object value = redisTemplate.opsForValue().get("sk:"+ pid +":count");
            //秒杀是否开始
            if(StringUtils.isEmpty(value)){
                System.err.printf("秒杀未开始,敬请期待");
                return;
            }
            // 秒杀是否结束
            if((int)value == 0){
                System.err.println("秒杀已经结束");
                return;
            }
            // 是否重复秒杀
            Set members = redisTemplate.opsForSet().members("sk:" + pid + ":user");
            if(members.contains(userid)) {
                System.err.println("已经参与秒杀,请勿重复参与!");
                return;
            }
            // 秒杀开始
            redisTemplate.opsForValue().set("sk:"+ pid +":count", (int)value -1);
            redisTemplate.opsForSet().add("sk:"+ pid +":user", userid);
            System.out.println("秒杀成功!");
            //2.2有值就转成成int
//            int num = Integer.parseInt(value+"");
            //2.3把redis的num加1
//            redisTemplate.opsForValue().set("num", ++num);
            //2.4释放锁,del
            // todo 判断当前释放的锁是当前加上的锁
            String lock1 = redisTemplate.opsForValue().get("lock").toString();
            if(uuid.equals(lock1)){
                redisTemplate.delete("lock");
            }
        }else{
            //3 获取锁失败、每隔0.1秒再获取
            try {
                Thread.sleep(100);
                testLock(pid, userid);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

四、使用redission操作redis,解决秒杀问题(公司使用的是这种方式,只需要将redisTemplate换成redission即可)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值