1、引入依赖和配置

引入spring-boot-starter-data-redis依赖

 <!--redis-->
 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

配置文件中配置redis连接信息

spring:
  redis: # redis配置
    host: 127.0.0.1
    port: 6379
    database: 0
    lettuce:
      pool:
        # 连接池最大连接数 默认8 ,负数表示没有限制
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
        max-wait: -1
        # 连接池中的最大空闲连接 默认8
        max-idle: 8
        # 连接池中的最小空闲连接 默认0
        min-idle: 0

配置RedisTemplate

@Configuration
public class LettuceConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // 使用fastjson作为序列化器,需要引入相关依赖
        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 一般redis的key为String, value为Object
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

2、使用LUA脚本解决库存扣减的原子性问题

这里的是productCode对应的库存提前预加载到了Redis中,扣减时从Redis中扣减,需要想获取当前库存,判断库存是否满足扣减条件,满足时进行库存扣减。但是这个过程涉及到查询而后更新,不是原子的,在大并发下场景下会出现数据不一致问题,使用LUA脚本来解决。

public DataResponse<Object> panicBuying(String productCode, Integer quantity) {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        //设置返回值类型
        redisScript.setResultType(Long.class);
        //设置lua脚本文件路径
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/panicBuying.lua")));
        List<String> keys = new ArrayList<>();
        keys.add(productCode);
        Long resCode = (Long)redisTemplate.opsForHash().getOperations().execute(redisScript, keys, quantity);
        Assert.isTrue(resCode!=null,"execute lua script error");
        String msg="success to decrement stock";
        if(resCode==1) {
            msg = "stock is not enough";
        } else if(resCode==2) {
            msg = "product not exist";
        }
        return new DataResponse<>(msg);
    }

LUA脚本内容如下:

local productCode=KEYS[1]
local buyQty = tonumber(ARGV[1])

local productExist = redis.call('exists',productCode)
redis.log(redis.LOG_DEBUG, productExist)

if productExist == 1 then
    local curStock = tonumber(redis.call('get',productCode))
    redis.log(redis.LOG_DEBUG, curStock)
    if curStock > 0 and curStock-buyQty >= 0 then
        redis.call('decrby',productCode,buyQty)
        return 0;
    else
        return 1;
    end
else
    return 2;
end
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐