使用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工具类 这是一个大哥写的,但是当时手贱我删掉了,蛮好用的
-
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
-
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; } }
-
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);