Redis(10) -- 不考虑并发的秒杀案例实现

本文详细介绍了使用Redis 6进行秒杀活动的事务操作,包括库存减一、用户记录及并发控制。通过实例展示了如何避免超卖和连接超时问题,并探讨了并发测试中遇到的问题与解决方案。

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

Redis6

9.3)Redis的事务秒杀案例

9.3.1)解决计数器和人员记录的事务操作

秒杀主要包括两个操作:1)商品库存 - 1 2)秒杀成功的该用户加到秒杀成功者清单里

 9.3.2)不考虑并发的秒杀案例实现

新建工程 Seckill ,这是一个WEB工程

创建页面:index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>iPhone 13 Pro !!!  1元秒杀!!!
</h1>
​
<form id="msform" action="${pageContext.request.contextPath}/doseckill" enctype="application/x-www-form-urlencoded">
    <%--固定商品id设置为0101--%>
    <input type="hidden" id="prodid" name="prodid" value="0101">
    <input type="button"  id="miaosha_btn" name="seckill_btn" value="秒杀点我"/>
</form>
​
</body>
<script  type="text/javascript" src="${pageContext.request.contextPath}/script/jquery/jquery-3.1.0.js"></script>
<script  type="text/javascript">
$(function(){
    $("#miaosha_btn").click(function(){
        var url=$("#msform").attr("action");
         $.post(url,$("#msform").serialize(),function(data){
            if(data=="false"){
                alert("抢光了" );
                $("#miaosha_btn").attr("disabled",true);
            }
        } );
    })
})
</script>
</html>

创建 SecKillServlet,页面按钮点击秒杀调用doPost()方法

public class SecKillServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
​
    public SecKillServlet() {
        super();
    }
​
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 通过随机数生成用户id
        String userid = new Random().nextInt(50000) + "";
        // 接收调用的商品id
        String prodid = request.getParameter("prodid");
        // 调用秒杀方法
        boolean isSuccess = SecKill_redis.doSecKill(userid, prodid);
        // 根据秒杀的返回结果返回相应信息
        response.getWriter().print(isSuccess);
    }
}

创建 SecKill_redis.java,秒杀主方法,秒杀的全流程在这里开发

public class SecKill_redis {
​
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 11079);
        System.out.println(jedis.ping());
        jedis.close();
    }
​
    //秒杀主方法--全过程
    public static boolean doSecKill(String uid, String prodid) throws IOException {
        //步骤1 根据入参用户ID-uid和商品ID-prodid 进行非空判断
        if (uid == null || prodid == null) {
            return false;
        }
​
        //步骤2 连接redis
        Jedis jedis = new Jedis("127.0.0.1", 11079);
​
        //步骤3 拼接key
        // 3.1 库存key
        String kcKey = "sk:" + prodid + ":qt";
        // 3.2 秒杀成功的用户key
        String userKey = "sk:" + prodid + ":user";
​
        // 步骤4 获取库存,如果库存null,秒杀还没有开始
        String kc = jedis.get(kcKey);
        if (kc == null) {
            System.out.println("秒杀还没有开始,请等待");
            jedis.close();
            return false;
        }
​
        // 步骤5 判断用户是否重复秒杀操作
        // 判断set集合中的用户信息,因为不能存在重复的用户【不能重复秒杀】所以使用Set存储用户信息
        if (jedis.sismember(userKey, uid)) {
            System.out.println("已经秒杀成功了,不能重复秒杀");
            jedis.close();
            return false;
        }
​
        // 步骤6 判断如果商品数量,库存数量小于1,秒杀结束
        if (Integer.parseInt(kc) <= 0) {
            System.out.println("秒杀已经结束了");
            jedis.close();
            return false;
        }
​
        // 步骤7 秒杀的过程
        //7.1 库存-1
        jedis.decr(kcKey);
        //7.2 把秒杀成功用户添加清单里面
        jedis.sadd(userKey, uid);
​
        System.out.println("秒杀成功了..");
        jedis.close();
        return true;
    }
}

测试:启动Tomcat工程

浏览器访问:http://localhost:8080/Seckill/

点击 “秒杀按钮” ,此时还未向 Redis 中添加 商品信息,效果如下图:

 向Redis中加入商品信息,命令如下:【添加了5个商品】

127.0.0.1:11079> flushdb
OK
127.0.0.1:11079> keys *
(empty array)
127.0.0.1:11079> set sk:0101:qt 5
OK
127.0.0.1:11079> keys *
1) "sk:0101:qt"

刷新页面后再次点击秒杀按钮”,效果如下图:

 弹框不再弹出,且后台提示 —— 秒杀成功了..

在Redis中查看相关商品和用户信息:

127.0.0.1:11079> get sk:0101:qt
"4"
127.0.0.1:11079> smembers sk:0101:user
1) "452"

发现商品数量已减少1,用户列表新增了秒杀成功的用户

再次点击秒杀按钮”,直到第六次,效果如下图:

 弹框再次弹出,且后台提示 —— 秒杀已经结束了

在Redis中查看相关商品和用户信息:

127.0.0.1:11079> get sk:0101:qt
"0"
127.0.0.1:11079> smembers sk:0101:user
1) "452"
2) "16928"
3) "27602"
4) "41023"
5) "41347"

发现商品信息已经为0,同时用户列表中已经有了5个秒杀成功的用户

并发测试:

在Linux系统中安装:

yum install httpd-tools

通过ab进行测试

在根目录新建 postfile 文件,内容如下:

prodid=0101&

在根目录执行下列命令,执行并发测试

ab -n 1000 -c 100  -p ~/postfile -T application/x-www-form-urlencoded http://172.19.83.237:8080/Seckill/doseckill

【1000个请求、100个并发,调用172.19.83.237(个人主机)上的秒杀方法】

输出:

存在问题: 

  • 已经秒杀结束了,但还存在秒杀成功的情况

  • 【超卖问题】Redis 中的商品信息为 -3,不能出现商品为负数的情况

  • 【连接超时问题】Redis 无法同时处理过多的请求,不能处理的请求需要等待,等待时间过长会报连接超时错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值