Redis(二十二)-秒杀案例的基本实现以及用ab工具模拟并发

Posted 码农飞哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis(二十二)-秒杀案例的基本实现以及用ab工具模拟并发相关的知识,希望对你有一定的参考价值。

您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦
💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通
❤️ 2. Python爬虫专栏,系统性的学习爬虫的知识点。9.9元买不了吃亏,买不了上当 。python爬虫入门进阶
❤️ 3. Ceph实战,从原理到实战应有尽有。 Ceph实战
❤️ 4. Java高并发编程入门,打卡学习Java高并发。 Java高并发编程入门
😁 5. 社区逛一逛,周周有福利,周周有惊喜。码农飞哥社区,飞跃计划
全网同名【码农飞哥】欢迎关注,个人VX: wei158556

文章目录

简介

上一篇文章我们介绍了悲观锁和乐观锁,Redis(二十一)-Redis的事务冲突(悲观锁和乐观锁)。这篇文章我们接着来学习Redis,本文主要介绍如何通过Redis来实现秒杀,以及如何通过ab工具来模拟并发。

秒杀案例的基本实现

商品秒杀是电商系统中的一个常见场景,就是商家将拿出少量某商品以特价的方式,在有限的时间内进行销售。在秒杀的场景中需要注意的情况是:1. 商品不能超卖,2. 同一个人只能抢一次。所以,

  1. 秒杀的基本实现思路有如下七个步骤:
//uid 是用户ID
// prodid 是商品ID
 public boolean doSeckill(String uid, String prodid) 
        // 1.uid和prodid非空判断
        if (StringUtils.isAnyBlank(uid, prodid)) 
            System.out.println("uid和prodid为空");
            return false;
        
        // 2.连接redis
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        // 3.拼接key
        // 3.1. 库存key
        String skKey = "sk:" + prodid + ":qt";
        // 3.2. 秒杀成功用户key
        String userKey = "sk:" + prodid + ":user";
        // 4.获取库存,如果库存null,秒杀还没有开始
        String skValue = jedis.get(skKey);
        if (skValue == null) 
            System.out.println("秒杀还没开始,请等待");
            jedis.close();
            return false;
        
        // 5.判断用户是否重复操作
        Boolean sismember = jedis.sismember(userKey, uid);
        if (sismember) 
            System.out.println("该用户已经抢过一次,请勿重复抢购");
            jedis.close();
            return false;
        
        // 6. 判断如果商品数量,库存数量小于1,秒杀结束
        if (Integer.parseInt(skValue) <= 0) 
            System.out.println("该商品的库存不足,秒杀失败");
            jedis.close();
            return false;
        
        // 7. 秒杀过程
        //    7.1. 库存-1
        jedis.decr(skKey);
        //    7.2. 用户加入到set集合中
        jedis.sadd(userKey, uid);
        System.out.println("秒杀成功");
        return true;
    
  • 为了防止某个用户重复抢,所以需要将抢过的用户id放在set 这个数据类型中,在每次请求是需要判断该set中有没有该用户。
  • 为了防止超卖的情况,所以,在每次请求之前都需要查看一下当前的商品数量,如果商品数量等于0则返回库存不足,用户成功抢到商品之后需要将库存-1。
  1. 定义的测试接口是:
    @RequestMapping("/skill/do")
    public boolean setandGetValue() 
        String prodid = "1010";
        StringBuilder uidSB = new StringBuilder("");
        for (int i = 0; i < 6; i++) 
            int nextInt = new Random().nextInt(10);
            uidSB.append(nextInt);
        
        boolean result = seckillService.doSeckill(uidSB.toString(), prodid);
        return result;
    
  1. 在redis中设置秒杀商品的数量为10个。设置的命令是:set sk:1010:qt 10

    请求地址是:http://10.41.152.17:8080/seckill/skill/do。其中seckill是项目名。单个请求的测试结果如下图所示:

ab工具模拟并发

这里通过Linux的ab工具模拟并发测试,ab工具的安装使用非常简单。

安装ab工具

在联网的情况下只需要通过 yum install httpd-tools 命令即可安装ab工具。

安装完成之后通过ab --help 命令可以查看ab命令的各个参数的含义。

其中最常用的参数是:

-n  最大的请求数
-c  同时最大的并发数
-p postfile  指定post请求的参数
-T 指定请求的content-type

可以通过vim postfile 模拟表单提交参数,以&符号结尾,存放在当前目录,输入内容 prodid=1010& (PS: 在本次测试中实际上没有到)
输入下面的测试命令测试一波,该命令指定了最大请求数是1000个,并发数是100个,10.41.152.17 是你本机的IP地址。

ab -n 1000 -c 100 -p postfile -T 'application/x-www-form-urlencoded' http://10.41.152.17:8080/seckill/skill/do

在测试之前依然通过set sk:1010:qt 10 命令设置10个秒杀商品qt。
执行结果如下三个图片所示:



从上述两个图片可以看出此基本实现过程有如下几个问题:

  1. 前面的线程都提示 商品库存不足,秒杀失败,后面的线程还能提示 秒杀成功,检查商品的数量发现商品数量为-6,也就是说出现了超卖的情况。
  2. 后面的jedis客户端请求会出现 connect timed out 连接超时的情况。
    这两个问题的解决方案会在后续的文章中详细介绍。

总结

本文通过jedis客户端简单实现了秒杀的案例,并通过ab工具模拟了并发测试。但是,由于代码设计考虑不周,会出现超卖以及jedis客户端会出现连接超时的情况。

以上是关于Redis(二十二)-秒杀案例的基本实现以及用ab工具模拟并发的主要内容,如果未能解决你的问题,请参考以下文章

Redis(二十四)-秒杀案例之库存遗留问题解决

Redis(二十四)-秒杀案例之库存遗留问题解决

Redis(二十三)-秒杀案例之超卖和超时问题解决

Redis(二十三)-秒杀案例之超卖和超时问题解决

尚硅谷Redis学习笔记-- Redis秒杀案例

React学习案例二十二