商品订单高并发环境下秒杀案例设计与分析

商品订单高并发环境下秒杀案例设计与分析

秒杀代码:

package com.micah.msgcenter.service.impl;

import com.micah.msgcenter.service.StoreService;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

/**
 * @Author m.kong
 * @Date 2021/7/7 下午4:00
 * @Version 1
 * @Description
 */
@Service
public class StoreServiceImpl implements StoreService {

    @Override
    public boolean spike(String userId, String goodsId){
        //1、参数判断
        if (userId == null || goodsId == null){
            System.out.println("秒杀失败");
            return false;
        }

        //2、连接redis
        Jedis jedis = new Jedis("www.daxiaobai.site", 6379);
        jedis.auth("micah_redis");
        //3、拼接key
        String storeKey = "store:" + goodsId + ":count";
        String userKey = "order:" + goodsId + ":user";
        //4、检查库存,库存为空,说明秒杀还未开始
        String storeCount = jedis.get(storeKey);
        if (storeCount == null){
            System.out.println("秒杀还未开始");
            jedis.close();
            return false;
        }

        //5、判断用户是否重复秒杀
        if (jedis.sismember(userKey, userId)) {
            System.out.println("已经参与过秒杀");
            jedis.close();
            return false;
        }

        //6、秒杀数量 <= 0 秒杀已经结束
        if (Integer.parseInt(jedis.get(storeKey)) <= 0){
            System.out.println("秒杀已经结束");
            jedis.close();
            return false;
        }

        //7、秒杀过程
        //7.1 库存减1
        jedis.decr(storeKey);
        //7.2 存入订单
        jedis.sadd(userKey, userId);

        System.out.println("恭喜您,秒杀成功");
        jedis.close();
        return true;
    }
}

控制台输出日志:

恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
恭喜您,秒杀成功
秒杀已经结束
恭喜您,秒杀成功
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束
秒杀已经结束

redis服务器端指令:

127.0.0.1:6379> ab -n 2000 -c 200 -k -T application/x-www-form-urlencoded http://www.daxiaobai.site:8888/redis/spike?goodsId=0101
[root@VM-4-4-centos ~]# ab -n 2000 -c 200 -k -T application/x-www-form-urlencoded http://www.daxiaobai.site:8888/redis/spike?goodsId=0101
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.daxiaobai.site (be patient)
Completed 200 requests
Completed 400 requests
Completed 600 requests
Completed 800 requests
Completed 1000 requests
Completed 1200 requests
Completed 1400 requests
Completed 1600 requests
Completed 1800 requests
Completed 2000 requests
Finished 2000 requests


Server Software:        
Server Hostname:        www.daxiaobai.site
Server Port:            8888

Document Path:          /redis/spike?goodsId=0101
Document Length:        12 bytes

Concurrency Level:      200
Time taken for tests:   5.668 seconds
Complete requests:      2000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    2000
Total transferred:      348000 bytes
HTML transferred:       24000 bytes
Requests per second:    352.89 [#/sec] (mean)
Time per request:       566.756 [ms] (mean)
Time per request:       2.834 [ms] (mean, across all concurrent requests)
Transfer rate:          59.96 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   46 207.9      0    1009
Processing:    44  512 533.1    319    2965
Waiting:       44  512 533.1    319    2965
Total:         50  558 623.0    319    3137

Percentage of the requests served within a certain time (ms)
  50%    319
  66%    363
  75%    368
  80%    417
  90%   1570
  95%   2330
  98%   2680
  99%   2816
 100%   3137 (longest request)
[root@VM-4-4-centos ~]# 

2 超卖问题

通过乐观锁进行解决
在这里插入图片描述

3 连接超时问题

节省每次连接redis服务带来的消耗,把连接好的实例反复利用。
通过参数管理连接的行为

链接池参数

MaxTotal:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了MaxTotal个jedis实例,则此时pool的状态为exhausted。
maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例;
MaxWaitMillis:表示当borrow一个jedis实例时,最大的等待毫秒数,如果超过等待时间,则直接抛JedisConnectionException;
testOnBorrow:获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;

package com.micah.msgcenter.utils;

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

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @Author m.kong
 * @Date 2021/7/8 上午11:22
 * @Version 1
 * @Description
 */
public class JedisPoolUtil {
    private static JedisPool jedisPool;

    static {
        try {
            // 读取配置文件
            Properties pro = new Properties();
            InputStream is = JedisPoolUtil.class.getClassLoader().getResourceAsStream("jedis_config.properties");
            pro.load(is);

            // 创建jedis连接池配置对象
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
            jedisPoolConfig.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));

            // 使用jedis连接池配置对象创建jedis连接池配置对象
            jedisPool = new JedisPool(jedisPoolConfig, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")), Integer.parseInt(pro.getProperty("timeout")),  pro.getProperty("auth"));
            System.out.println("连接成功");
        } catch (IOException e) {
            System.out.println("连接失败");
            e.printStackTrace();
        }
    }

    /**
     * 获取jedis连接
     */
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 释放资源
     */
    public static void close(final Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

    public static void main(String[] args) {
        JedisPoolUtil.getJedis();
        Jedis jedis = JedisPoolUtil.jedisPool.getResource();
        System.out.println(jedis.get("store:0101:count"));
    }
}

4、库存遗留问题

使用嵌入lua脚本来解决这一问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值