【Redis学习笔记】秒杀小案例(Jedis)

使用springboot+jedis整合秒杀案例

  1. 需要引入的依赖
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
  1. 业务代码
package springboot.redis.controller;

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.util.Random;

@RestController
public class JedisDemoController {


    @GetMapping("jedisSecKill")
    public String secKill() {
        StringBuilder phone = new StringBuilder("155");
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int nextInt = random.nextInt(10);
            phone.append(nextInt);
        }
        boolean b = doSecKill(phone.toString(), "0101");
        return b ? "success" : "fail";
    }

    public boolean doSecKill(String phone, String productId) {
        Jedis jedis = new Jedis("110.40.194.18", 6379);
        jedis.auth("redis123");
        if (StringUtils.isEmpty(phone)) {
            System.out.println("手机号不能为空");
            return false;
        }
        String userKey = "kc:" + productId + ":user";
        String kcKey = "kc:" + productId;
        String kc = jedis.get(kcKey);
        System.out.println("kc = " + kc);
        if (StringUtils.isEmpty(kc)) {
            System.out.println("秒杀还未开始,请耐心等待!");
            return false;
        }
        int num = Integer.parseInt(kc);
        if (num <= 0) {
            System.out.println("秒杀已经结束,请下次再来!");
            return false;
        }
        if (jedis.sismember(userKey,phone)){
            System.out.println("已经参加过活动,不可重复参加");
            return false;
        }
        jedis.decr(kcKey);
        jedis.sadd(userKey, phone);
        System.out.println("秒杀成功!");
        return true;
    }
}
  1. 使用JMeter进行并发测试
    在这里插入图片描述

发现会产生负数

在这里插入图片描述
4. 通过watch和redis的事务支持来解决这个问题;

package springboot.redis.controller;

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

import java.util.Random;

@RestController
public class JedisDemoController {


    @GetMapping("jedisSecKill")
    public String secKill() {
        StringBuilder phone = new StringBuilder("155");
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int nextInt = random.nextInt(10);
            phone.append(nextInt);
        }
        boolean b = doSecKill(phone.toString(), "0101");
        return b ? "success" : "fail";
    }

    public boolean doSecKill(String phone, String productId) {
        Jedis jedis = new Jedis("110.40.194.18", 6379);
        jedis.auth("redis123");
        if (StringUtils.isEmpty(phone)) {
            System.out.println("手机号不能为空");
            return false;
        }
        String userKey = "kc:" + productId + ":user";
        String kcKey = "kc:" + productId;
        jedis.watch(kcKey);
        String kc = jedis.get(kcKey);
        System.out.println("kc = " + kc);
        if (StringUtils.isEmpty(kc)) {
            System.out.println("秒杀还未开始,请耐心等待!");
            return false;
        }
        int num = Integer.parseInt(kc);
        if (num <= 0) {
            System.out.println("秒杀已经结束,请下次再来!");
            return false;
        }
        if (jedis.sismember(userKey,phone)){
            System.out.println("已经参加过活动,不可重复参加");
            return false;
        }
        Transaction multi = jedis.multi();
        multi.decr(kcKey);
        multi.sadd(userKey,phone);
        List<Object> exec = multi.exec();
        if (exec == null || exec.size()==0){
            System.out.println("秒杀失败");
            return false;
        }
//        jedis.decr(kcKey);
//        jedis.sadd(userKey, phone);
        System.out.println("秒杀成功!");
        return true;
    }
}

但是当我加大并发数量加到2000和秒杀商品的数量加到100时,会发现

在这里插入图片描述
5. 使用Lua脚本解决库存遗留问题

package springboot.redis.controller;

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

import java.util.Random;

@RestController
public class JedisDemoController {

    static String secKillString= "local userid=KEYS[1];\r\n" +
            "local prodid=KEYS[2];\r\n" +
            "local kckey='kc:'..prodid;\r\n" +
            "local userKey='kc:'..prodid..':user';\r\n" +
            "local userExists=redis.call('sismember',userKey,userid);\r\n" +
            "if tonumber(userExists)==1 then \r\n" +
            "    return 2;" +
            "end\r\n" +
            "local num= redis.call(\"get\" ,kckey);\r\n" +
            "if tonumber(num)<=0 then \r\n" +
            "    return 0;\r\n" +
            "else \r\n" +
            "    redis.call(\"decr\",kckey);\r\n" +
            "    redis.call(\"sadd\",userKey,userid);\r\n" +
            "end \r\n" +
            "return 1;";

    @GetMapping("jedisSecKill")
    public String secKill() {
        StringBuilder phone = new StringBuilder("155");
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int nextInt = random.nextInt(10);
            phone.append(nextInt);
        }
        boolean b = doSecKill(phone.toString(), "0101");
        return b ? "success" : "fail";
    }

    public boolean doSecKill(String phone, String productId) {
        Jedis jedis = new Jedis("110.40.194.183", 6379);
        jedis.auth("redis123");
        if (StringUtils.isEmpty(phone)) {
            System.out.println("手机号不能为空");
            return false;
        }
        String scriptLoad = jedis.scriptLoad(secKillString);
        Object evalsha = jedis.evalsha(scriptLoad, 2, phone, productId);
        String result = String.valueOf(evalsha);
        if ("0".equals(result)){
            System.out.println("已抢光");
            return false;
        }else if ("1".equals(result)){
            System.out.println("抢购成功");
            return false;
        } else if ("2".equals(result)) {
            System.out.println("一个用户只能抢购一次");
            return false;
        }else {
            System.out.println("抢购异常");
            return false;
        }
    }
}

有时会发生连接超时

在这里插入图片描述
6. 使用Redis连接池解决超时问题


package springboot.redis.conf;

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolUtil {

    private static volatile JedisPool jedisPool = null;

    private JedisPoolUtil() {
    }

    public static JedisPool getJedisPool() {
        if (jedisPool == null) {
            synchronized (JedisPoolUtil.class) {
                if (jedisPool == null) {
                    JedisPoolConfig config = new JedisPoolConfig();
                    config.setMaxTotal(200);
                    config.setMaxIdle(32);
                    config.setMaxWaitMillis(1000 * 100);
                    config.setBlockWhenExhausted(true);
                    config.setTestOnBorrow(true);
                    jedisPool = new JedisPool(config, "110.40.194.18", 6379, 6000);
                }
            }
        }
        return jedisPool;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值