网站用的服务器是什么,排版设计是什么意思,帝国手机网站cms系统,做网站虚拟主机怎么选择SpringBootredislua 防止超卖
一、背景
工作中遇到了有人用 RedisTemplate 的 increment去做总库存的加减#xff0c;但是这种方式是保证不了原子性的还是会超卖。
redis 是可以保证原子性#xff0c;但是 RedisTemplate 里面的方法去调用redis是不能保证原子性
二、优化…SpringBootredislua 防止超卖
一、背景
工作中遇到了有人用 RedisTemplate 的 increment去做总库存的加减但是这种方式是保证不了原子性的还是会超卖。
redis 是可以保证原子性但是 RedisTemplate 里面的方法去调用redis是不能保证原子性
二、优化方案
使用 lua 脚本去执行 加减操作执行 redis 的命令来保证原子性
三、重点代码
RedisTemplate 注入
Configuration
public class RedisConfig {SuppressWarnings({rawtypes, unchecked})Bean(name redisTemplate)public RedisTemplateString, Object redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {RedisTemplateString, Object redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(lettuceConnectionFactory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}抢购帮助类并支持集群模式
/*** Author liyue* date 2024/3/22 14:31**/
Component
public class SecKillProvider {private static final String PRODUCT_KEY {cluster:}productstock;private static final String SECKILL_SCRIPT lua/seckill.lua;Resourceprivate RedisTemplateString, Object redisTemplate;public void initStock(int stock) {//24小时过期RedisUtils.setIfAbsentTimeout(PRODUCT_KEY, stock, 86400);}// DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN))public boolean seckill(String userId) {//调用lua脚本并执行DefaultRedisScriptLong redisScript new DefaultRedisScript();redisScript.setResultType(Long.class);//返回类型是Long//lua文件存放在resources目录下的redis文件夹内redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(SECKILL_SCRIPT)));Integer result (Integer) redisTemplate.opsForValue().get(PRODUCT_KEY);System.out.println(result);System.out.println(PRODUCT_KEY);Long stock redisTemplate.execute(redisScript, Arrays.asList(PRODUCT_KEY));System.out.println(执行完成----stock stock);if (stock 0) {// 抢购成功可以继续处理订单等逻辑System.out.println(User userId seckill success!);return true;} else {// 抢购失败库存不足System.out.println(User userId seckill failed!);return false;}}
}lua 脚本
local stock tonumber(redis.call(get, KEYS[1]))
if stock and stock 0 thenredis.call(decr, KEYS[1])return stock - 1
elsereturn -1
end并发测试类
/*** Author liyue* date 2024/3/22 15:14**/
RunWith(SpringRunner.class)
SpringBootTest
Slf4j
public class SecKillProviderTest {Resourceprivate SecKillProvider secKillProvider;Testpublic void t() throws InterruptedException {secKillProvider.initStock(10);for (int i 0; i 2000; i) {new Thread(new Mythread(i)).start();}Thread.sleep(10000);}class Mythread implements Runnable {private int num;Mythread(int num) {this.num num;}SuppressWarnings(unchecked)SecKillProvider secKillProvider SpringUtil.getBean(secKillProvider);Overridepublic void run() {secKillProvider.seckill(String.valueOf(num));}}
}
总结
使用这种方式读取文件可以优化成sha的方式去去读取。后面再进行调整测试类可以模拟并发情况。测试没有什么问题。
本文由mdnice多平台发布