Redis6

9.3)Redis的事务秒杀案例

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

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

秒杀redis库存用什么类型 redis incr 秒杀_html

 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库存用什么类型 redis incr 秒杀_System_02

 向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库存用什么类型 redis incr 秒杀_html_03

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

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

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

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

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

秒杀redis库存用什么类型 redis incr 秒杀_redis_04

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

在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库存用什么类型 redis incr 秒杀_System_05

存在问题: 

  • 已经秒杀结束了,但还存在秒杀成功的情况
  • 【超卖问题】Redis 中的商品信息为 -3,不能出现商品为负数的情况
  • 【连接超时问题】Redis 无法同时处理过多的请求,不能处理的请求需要等待,等待时间过长会报连接超时错误