使用redis做无锁秒杀

使用redis做秒杀无需锁

简单说明下

使用Redis中hsah中的 hincrby 方法 k1=商品Id vk=skuId vv=库存数量 该操作有返回值 自增或自减后的 结果进行判断

hash解构

​ -k: 商品id

​ -k:商品sku1 ID -v:对应库存100

​ -k:商品sku2 ID -v:对应库存20

​ -k:商品sku3 ID -v:对应库存30

​ -k:商品sku4 ID -v:对应库存40

HINCRBY 1 1 -1 redis操作命令 自减1

“99” 返回结果 “99”

原理就是这样

这里使用的是Jedis

		<!-- Redis-Jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

Jedis工具类 这是一个大哥写的,但是当时手贱我删掉了,蛮好用的

  1. redis配置文件 没有的参数就不要乱填写 否则项目正常启动但是 会获取不到资源

    # redis 配置
    redis:
      # 地址
      host: 127.0.0.1
      # 端口,默认为6379
      port: 6379
      # 数据库索引
      database: 0
      # 密码(没有就不要填写了)
      password:
      # 连接超时时间
      timeout: 10000
      pool:
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 200
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1
    
  2. Jedis配置,项目注入JedisPool池

    package com.dog.common.config.jedis;
    
    import com.dog.common.utils.SUtils;
    import lombok.Data;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    /**
     * Jedis配置,项目启动注入JedisPool
     * http://www.cnblogs.com/GodHeng/p/9301330.html
     *
     * @author dolyw.com
     * @date 2018/9/5 10:35
     */
    @Configuration
    @EnableAutoConfiguration
    @ConfigurationProperties(prefix = "redis")
    @Data
    public class JedisConfig {
    
        /**
         * logger
         */
        private static final Logger logger = LoggerFactory.getLogger(JedisConfig.class);
        /**
         * 地址
         **/
        private String host;
        /**
         * 端口
         **/
        private int port;
        /**
         * 密码
         **/
        private String password;
    
        /**
         * 连接超时时间
         **/
        private int timeout;
    
        /**
         * 数据库索引 ,默认是0
         **/
        private int database=0;
    
        @Value("${redis.pool.max-active}")
        private int maxActive;
    
        @Value("${redis.pool.max-wait}")
        private int maxWait;
    
        @Value("${redis.pool.max-idle}")
        private int maxIdle;
    
        @Value("${redis.pool.min-idle}")
        private int minIdle;
    
        @Bean
        public JedisPool redisPoolFactory() {
            try {
                JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                jedisPoolConfig.setMaxIdle(maxIdle);
                jedisPoolConfig.setMaxWaitMillis(maxWait);
                jedisPoolConfig.setMaxTotal(maxActive);
    
                // 密码为空设置为null
                if (SUtils.isEmpty(password)) {
                    password = null;
                }
                JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password,database);
                logger.info("初始化Redis连接池JedisPool成功!地址: " + host + ":" + port +":"+"数据库索引"+database);
                return jedisPool;
            } catch (RuntimeException e) {
                logger.error("初始化Redis连接池JedisPool异常:" + e.getMessage());
            }
            return null;
        }
    }
    
    
  3. JedisUtil

    package com.dog.common.utils;
    
    
    import com.dog.common.constant.Constant;
    import com.dog.common.exception.CustomException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    import java.util.Set;
    
    /**
     * redis工具类
     */
    @Component
    //@Slf4j
    public class JedisUtil implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext = null;
        public JedisUtil() {
        }
    
        /**
         * 获取redis实例
         * @return
         */
        public Jedis getJedis() {
            Jedis jedis = null;
            synchronized (Jedis.class) {
                try {
                    jedis = getJedisPool().getResource();
                } finally {
                    if (jedis != null) {
                        jedis.close();
                    }
                }
            }
            return jedis;
        }
    
        public JedisPool getJedisPool() {
            JedisPool jedisPool = null;
            if (jedisPool == null) {
                synchronized (JedisPool.class) {
                    if (jedisPool == null) {
                        jedisPool = applicationContext.getBean(JedisPool.class);
                    }
                }
            }
            return jedisPool;
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (JedisUtil.applicationContext == null) {
                JedisUtil.applicationContext = applicationContext; //初始化 spring applicationContext
            }
        }
    
        /**
         * 根据key查看是否存在
         *
         * @param key
         * @return
         */
        public boolean hasKey(String key) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.exists(key);
            }catch (Exception e){
                //log.info("JedisUtil.hasKey异常:"+e.getMessage());
                return false;
            }finally {
                disConnect(jedis);
            }
        }
    
        /**
         * 设置key -value 形式数据
         *
         * @param key
         * @param value
         * @return
         */
        public String set(String key, String value) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.set(key, value);
            }catch (Exception e){
                //log.info("JedisUtil.set(String key, String value)异常:"+e.getMessage());
                return null;
            }finally {
                disConnect(jedis);
            }
    
        }
    
        /**
         * 设置 一个过期时间
         *
         * @param key
         * @param value
         * @param timeOut 单位秒
         * @return
         */
        public String set(String key, String value, int timeOut) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.setex(key, timeOut, value);
            }catch (Exception e){
                //log.info("JedisUtil.set(String key, String value, int timeOut)异常:"+e.getMessage());
                return null;
            }finally {
                disConnect(jedis);
            }
        }
    
        /**
         * 根据key获取value
         *
         * @param key
         * @return
         */
        public String getByKey(String key) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.get(key);
            }catch (Exception e){
                //log.info("JedisUtil.getByKey(String key)异常:"+e.getMessage());
                return null;
            }finally {
                disConnect(jedis);
            }
        }
    
        /**
         * 根据通配符获取所有匹配的key
         * @param pattern
         * @return
         */
        public Set<String> getKesByPattern(String pattern) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.keys(pattern);
            }catch (Exception e){
                //log.info("JedisUtil.getKesByPattern(String pattern)异常:"+e.getMessage());
                return null;
            }finally {
                disConnect(jedis);
            }
        }
    
        /**
         * 根据key删除
         * @param key
         */
        public void delByKey(String key) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                if (jedis.exists(key)) {
                    jedis.del(key);
                }
            }catch (Exception e){
                //log.info("JedisUtil.delByKey(String key)异常:"+e.getMessage());
            }finally {
                disConnect(jedis);
            }
        }
    
        /**
         * 根据key获取过期时间
         *
         * @param key
         * @return
         */
        public long getTimeOutByKey(String key) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.ttl(key);
            }catch (Exception e){
                //log.info("JedisUtil.getTimeOutByKey(String key)异常:"+e.getMessage());
                return 0;
            }finally {
                disConnect(jedis);
            }
    
        }
    
        /**
         * 清空数据 【慎用啊!】
         */
        public void flushDB(){
            Jedis jedis=null;
            try{
                jedis = getJedis();
                jedis.flushDB();
            }catch (Exception e){
                //log.info("JedisUtil.flushDB() 异常:"+e.getMessage());
            }finally {
                disConnect(jedis);
            }
    
        }
    
        /**
         * 刷新过期时间
         * @param key
         * @param timeOut
         * @return
         */
        public long refreshLiveTime(String key, int timeOut) {
            Jedis jedis=null;
            try{
                jedis = getJedis();
                return jedis.expire(key, timeOut);
            }catch (Exception e){
                //log.info("JedisUtil.hasKey异常:"+e.getMessage());
                return -1;
            }finally {
                disConnect(jedis);
            }
        }
    
        /**
         * 释放资源
         */
        public void disConnect(Jedis jedis) {
            if (jedis!=null){
                jedis.disconnect();
    //            jedis.close();
            }
        }
    
    
    
        /**
         * 静态注入JedisPool连接池
         * 本来是正常注入JedisUtil,可以在Controller和Service层使用,但是重写Shiro的CustomCache无法注入JedisUtil
         * 现在改为静态注入JedisPool连接池,JedisUtil直接调用静态方法即可
         * https://blog.csdn.net/W_Z_W_888/article/details/79979103
         */
        private static JedisPool jedisPool;
        @Autowired
        public void setJedisPool(JedisPool jedisPool) {
            JedisUtil.jedisPool = jedisPool;
        }
    
        /**
         * 获取Jedis实例
         * @param
         * @return redis.clients.jedis.Jedis
         * @author dog_E
         * @date 2018/9/4 15:47
         */
        public static synchronized Jedis getResource() {
            try {
                if (jedisPool != null) {
                    return jedisPool.getResource();
                } else {
                    return null;
                }
            } catch (Exception e) {
                throw new CustomException("获取Jedis资源异常:" + e.getMessage());
            }
        }
    
        /**
         * 释放Jedis资源
         * @param
         * @return void
         * @author dog_E
         * @date 2018/9/5 9:16
         */
        public static void closePool() {
            try {
                jedisPool.close();
            } catch (Exception e) {
                throw new CustomException("释放Jedis资源异常:" + e.getMessage());
            }
        }
    
        /**
         * 获取redis键值-object
         * @param key
         * @return java.lang.Object
         * @author dog_E
         * @date 2018/9/4 15:47
         */
        public static Object getObject(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                String bytes = jedis.get(key);
                if (SUtils.isNotBlank(bytes)) {
                    return bytes;
                }
            } catch (Exception e) {
                throw new CustomException("获取Redis键值getObject方法异常:key=" + key + " cause=" + e.getMessage());
            }
            return null;
        }
    
        /**
         * 设置redis键值-object
         * @param key
         * @param value
         * @return java.lang.String
         * @author dog_E
         * @date 2018/9/4 15:49
         */
        public static String setObject(String key, Object value) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.set(key.getBytes(), SerializableUtil.serializable(value));
            } catch (Exception e) {
                throw new CustomException("设置Redis键值setObject方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 设置redis键值-object-expiretime
         * @param key
         * @param value
         * @param expiretime
         * @return java.lang.String
         * @author dog_E
         * @date 2018/9/4 15:50
         */
        public static String setObject(String key, Object value, int expiretime) {
            String result;
            try (Jedis jedis = jedisPool.getResource()) {
                result = jedis.set(key, String.valueOf(value));
                if (Constant.RedisPrefixConstant.OK.equals(result)) {
                    jedis.expire(key.getBytes(), expiretime);
                }
                return result;
            } catch (Exception e) {
                throw new CustomException("设置Redis键值setObject方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 获取redis键值-Json
         * @param key
         * @return java.lang.Object
         * @author dog_E
         * @date 2018/9/4 15:47
         */
        public static String getJson(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.get(key);
            } catch (Exception e) {
                throw new CustomException("获取Redis键值getJson方法异常:key=" + key + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 设置redis键值-Json
         * @param key
         * @param value
         * @return java.lang.String
         * @author dog_E
         * @date 2018/9/4 15:49
         */
        public static String setJson(String key, String value) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.set(key, value);
            } catch (Exception e) {
                throw new CustomException("设置Redis键值setJson方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 设置redis键值-Json-expiretime
         * @param key
         * @param value
         * @param expiretime
         * @return java.lang.String
         * @author dog_E
         * @date 2018/9/4 15:50
         */
        public static String setJson(String key, String value, int expiretime) {
            String result;
            try (Jedis jedis = jedisPool.getResource()) {
                result = jedis.set(key, value);
                if (Constant.RedisPrefixConstant.OK.equals(result)) {
                    jedis.expire(key, expiretime);
                }
                return result;
            } catch (Exception e) {
                throw new CustomException("设置Redis键值setJson方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 删除key
         * @param key
         * @return java.lang.Long
         * @author dog_E
         * @date 2018/9/4 15:50
         */
        public static Long delKey(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.del(key.getBytes());
            } catch (Exception e) {
                throw new CustomException("删除Redis的键delKey方法异常:key=" + key + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 删除key
         * @param key
         * @return java.lang.Long
         * @author dog_E
         * @date 2018/9/4 15:50
         */
        public static Boolean delKeyBoolean(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    
        /**
         * key是否存在
         * @param key
         * @return java.lang.Boolean
         * @author dog_E
         * @date 2018/9/4 15:51
         */
        public static Boolean exists(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.exists(key);
            } catch (Exception e) {
                throw new CustomException("查询Redis的键是否存在exists方法异常:key=" + key + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 模糊查询获取key集合(keys的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,生产不推荐使用)
         * @param key
         * @return java.util.Set<java.lang.String>
         * @author dog_E
         * @date 2018/9/6 9:43
         */
        public static Set<String> keysS(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.keys(key);
            } catch (Exception e) {
                throw new CustomException("模糊查询Redis的键集合keysS方法异常:key=" + key + " cause=" + e.getMessage());
            }
        }
    
        /**
         * 模糊查询获取key集合(keys的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,生产不推荐使用)
         * @param key
         * @return java.util.Set<java.lang.String>
         * @author dog_E
         * @date 2018/9/6 9:43
         */
        public static Set<byte[]> keysB(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.keys(key.getBytes());
            } catch (Exception e) {
                throw new CustomException("模糊查询Redis的键集合keysB方法异常:key=" + key + " cause=" + e.getMessage());
            }
        }
    
    
        /**
         * redis做hash的添加
         */
        public static boolean hset(String key, String field, String value){
            if(SUtils.isBlank(key) || SUtils.isBlank(field)){
                return false;
            }
            try (Jedis jedis = jedisPool.getResource()) {
                //If the field already exists, and the HSET just produced an update of the value, 0 is returned,
                //otherwise if a new field is created 1 is returned.
                Long statusCode = jedis.hset(key, field, value);
                if(statusCode > -1){
                    return true;
                }
            } catch (Exception e) {
                throw new CustomException("模糊查询Redis的键集合keysB方法异常:key=" + key + " cause=" + e.getMessage());
            }
            return false;
        }
    
        /**
         * @Description: 获取hsah中field的value
         * @Author: @Dog_Elder
         * @Date: 2020/11/18 14:09
         * @param key: key
         * @param field: map中key
         * @return: map中value
         **/
        public static String hget(String key, String field){
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.hget(key, field);
            } catch (Exception e) {
                throw new CustomException("获取hsah中field的value方法异常:key=" + key + "field=" + field + " cause=" + e.getMessage());
            }
        }
    
    
        /**
         * @Description: hash 值 增或加
         * @Author: @Dog_Elder
         * @Date: 2020/11/19 14:35
         * @param key: key
         * @param field: map中key
         * @param value: 可以是正数 也可以是负数
         * @return: java.lang.Long
         **/
        public static Long hincrBy(String key, String field,long value){
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.hincrBy(key,field,value);
            } catch (Exception e) {
                throw new CustomException("获取hsah中field的value方法异常:key=" + key + "field=" + field + " cause=" + e.getMessage());
            }
        }
    
    
        /**
         * 获取过期剩余时间
         * @param key
         * @return java.lang.String
         * @author dog_E
         * @date 2018/9/11 16:26
         */
        public static Long ttl(String key) {
            Long result = -2L;
            try (Jedis jedis = jedisPool.getResource()) {
                result = jedis.ttl(key);
                return result;
            } catch (Exception e) {
                throw new CustomException("获取Redis键过期剩余时间ttl方法异常:key=" + key + " cause=" + e.getMessage());
            }
        }
    
    
    }
    
    

    测试代码

    ps:无需关系Redis中的值 只用看你的java代码的执行结果接口 保证是10个成功就可以了

            boolean result = JedisUtil.hset("1", "1", "10");
            if (result) {
                System.out.println("商品初始化成功,商品id为1,skuId为1,数量10");
            } else {
                System.out.println("商品初始化失败");
            }
            ExecutorService service = Executors.newFixedThreadPool(10);
            Thread runnable1 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    String stock = JedisUtil.hget("1", "1");
                    if (Integer.parseInt(stock) < 0) {
                        return;
                    }
                    Long count = JedisUtil.hincrBy("1", "1", -1);
                    if (count.intValue() < 0) {
                        return;
                    }
                    System.out.println("抢购成功");
                }
            });
            Thread runnable2 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    String stock = JedisUtil.hget("1", "1");
                    if (Integer.parseInt(stock) < 0) {
                        return;
                    }
                    Long count = JedisUtil.hincrBy("1", "1", -1);
                    if (count.intValue() < 0) {
                        return;
                    }
                    System.out.println("抢购成功");
                }
            });
            Thread runnable3 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    String stock = JedisUtil.hget("1", "1");
                    if (Integer.parseInt(stock) < 0) {
                        return;
                    }
                    Long count = JedisUtil.hincrBy("1", "1", -1);
                    if (count.intValue() < 0) {
                        return;
                    }
                    System.out.println("抢购成功");
                }
            });
            service.submit(runnable1);
            service.submit(runnable2);
            service.submit(runnable3);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String hget = JedisUtil.hget("1", "1");
            System.out.println(" redis 数量 = " + hget);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值