使用Redis实现秒杀业务

举报
小小张自由--张有博 发表于 2022/05/21 15:18:38 2022/05/21
【摘要】 使用Redis实现秒杀业务

使用redis的事务,商品数减减,用户信息使用Set集合


实现方式一

package com.promsing.jedis.seckill;

import redis.clients.jedis.Jedis;

import java.io.IOException;

/**
 * 模拟秒杀,未使用事务
 *
 * @author Promsing(张有博)
 * @version 1.0.0
 * @since 2022/5/7 - 17:13
 */
public class SeckillJedisV1 {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        System.out.println(jedis.ping());
        jedis.close();
    }

    //秒杀过程
    public static boolean doSecKill(String uid,String prodid) throws IOException {
        //1 uid和prodid非空判断
        if(uid == null || prodid == null) {
            return false;
        }

        //2 连接redis
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //通过连接池得到jedis对象

        //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 判断用户是否重复秒杀操作
        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;
    }
}

并发情况下存在:

1.超卖问题:,活动结束了,还能卖。

没有事务,两者同时拿到,都会售卖。

2.超时问题:redis连接数被占满,连接超时。


实现方式二 - 解决超卖超时


使用redis连接池

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

使用乐观锁+redis事务

import com.promsing.jedis.utils.JedisPoolUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.io.IOException;
import java.util.List;

/**
 * 秒杀,解决超卖、超时
 *
 * @author Promsing(张有博)
 * @version 1.0.0
 * @since 2022/5/8 - 14:05
 */
public class SeckillJedisV2 {


    //秒杀过程
    public static boolean doSecKill(String uid,String prodid) throws IOException {
        //1 uid和prodid非空判断
        if(uid == null || prodid == null) {
            return false;
        }

        //2 连接redis
        // Jedis jedis = new Jedis("127.0.0.1", 6379);
        //通过连接池得到jedis对象
        JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();

        //3 拼接key
        // 3.1 库存key
        String kcKey = "sk:"+prodid+":qt";
        // 3.2 秒杀成功用户key
        String userKey = "sk:"+prodid+":user";

        //监视库存
        jedis.watch(kcKey);

        //4 获取库存,如果库存null,秒杀还没有开始
        String kc = jedis.get(kcKey);
        if(kc == null) {
            System.out.println("秒杀还没有开始,请等待");
            jedis.close();
            return false;
        }

        // 5 判断用户是否重复秒杀操作
        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 秒杀过程
        //使用事务
        Transaction multi = jedis.multi();
        //库存-1
        multi.decr(kcKey);
        //把秒杀成功用户添加清单里面
        multi.sadd(userKey,uid);

        List<Object> results = multi.exec();

        if (results==null|| results.size()==0){
            System.out.println("秒杀失败");
            jedis.close();
            return false;
        }

        System.out.println("秒杀成功了..");
        jedis.close();
        return true;
    }


}



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

/**
 * Jedis连接池
 *
 * @author Promsing(张有博)
 * @version 1.0.0
 * @since 2022/5/8 - 14:08
 */
public class JedisPoolUtil {

    private static volatile JedisPool jedisPool = null;

    private JedisPoolUtil() {
    }

    public static JedisPool getJedisPoolInstance() {
        if (null == jedisPool) {
            synchronized (JedisPoolUtil.class) {
                if (null == jedisPool) {
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    poolConfig.setMaxTotal(200);
                    poolConfig.setMaxIdle(32);
                    poolConfig.setMaxWaitMillis(100*1000);
                    poolConfig.setBlockWhenExhausted(true);
                    poolConfig.setTestOnBorrow(true);  // ping  PONG

                    jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 60000 );
                }
            }
        }
        return jedisPool;
    }

    public static void release(JedisPool jedisPool, Jedis jedis) {
        if (null != jedis) {
            jedisPool.returnResource(jedis);
        }
    }
}

实现方式三 -解决库存遗留

出现原因:当有一个人修改了版本号,其他所有人都事务失败。因为你其余人都会核对版本号,当不一致,会事务失败

使用LUA脚本

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。