一、使用lua脚本扣减单个商品的库存
@SpringBootTest
class LuaTests {@AutowiredStringRedisTemplate stringRedisTemplate;@Testvoid test3() {for (int i = 1; i <= 5; i++) {stringRedisTemplate.opsForValue().set("product."+i,String.valueOf(i));}}@Testvoid test2() {StringBuilder sb = new StringBuilder();sb.append(" local key = KEYS[1] "); sb.append(" local qty = ARGV[1] "); sb.append(" local redis_qty = redis.call('get',key) "); sb.append(" if tonumber(redis_qty) >= tonumber(qty) then ");sb.append(" redis.call('decrby',key,qty) "); sb.append(" return -1 "); sb.append(" else ");sb.append(" return tonumber(redis_qty) "); sb.append(" end ");RedisScript<Long> luaScript = RedisScript.of(sb.toString(), Long.class);ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 1; i <= 5; i++) {executorService.execute(()->{int qty = RandomUtil.randomInt(1,6);Long count = stringRedisTemplate.execute(luaScript, CollUtil.newArrayList("product.5"),String.valueOf(qty));if (count == -1L) {System.out.println(Thread.currentThread().getId() + " 扣减成功,扣减了-> "+ qty);} else {System.out.println(Thread.currentThread().getId() + "扣减失败,需求量是:"+qty+",剩余库存量:"+count);}});}ThreadUtil.safeSleep(3000);}
}
二、使用lua脚本扣减多个商品的库存
@Testvoid test4() {StringBuilder sb = new StringBuilder();sb.append(" local table = {} "); sb.append(" local values = redis.call('mget', unpack(KEYS) )"); sb.append(" for i = 1, #KEYS do ");sb.append(" if tonumber(ARGV[i]) > tonumber(values[i]) then ");sb.append(" table[#table + 1] = KEYS[i] .. '=' .. values[i] "); sb.append(" end ");sb.append(" end ");sb.append(" if #table > 0 then ");sb.append(" return table ");sb.append(" end ");sb.append(" for i = 1 , #KEYS do ");sb.append(" redis.call('decrby',KEYS[i],ARGV[i]) ");sb.append(" end ");sb.append(" return {} ");RedisScript<List> luaScript = RedisScript.of(sb.toString(), List.class);List<StockProduct> stockProducts = new ArrayList<>();stockProducts.add(new StockProduct(5,1));stockProducts.add(new StockProduct(4,2));List<String> keys = stockProducts.stream().map(it -> "product." + it.getId()).collect(Collectors.toList());Object[] qtys = stockProducts.stream().map(it -> it.getQty() + "").toArray();List<String> list = stringRedisTemplate.execute(luaScript,keys,qtys);if(list.isEmpty()){System.out.println("库存冻结成功");} else {for (String key_qty : list) {String[] split = key_qty.split("=");System.out.println(split[0] + "库存不足,剩余库存量:" + split[1]);}}ThreadUtil.safeSleep(3000);}
三、通过分布式锁,扣减商品的库存
package com.by;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import com.by.moder.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;@SpringBootTest
class SetNXTests {@AutowiredStringRedisTemplate stringRedisTemplate;@Resource(name = "redisTemplate")ValueOperations<String,String> valueOperations;@Testvoid Test() {ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 1; i <=5; i++) {executorService.execute(()->{String ioId = "IO"+ RandomUtil.randomInt(1,1000);while (true){Boolean b = valueOperations.setIfAbsent("lock.product.1",ioId+":"+ DateUtil.now());if(b){System.out.println(Thread.currentThread().getId()+"获取到了分布式锁");ThreadUtil.safeSleep(3000);stringRedisTemplate.delete("lock.product.1");System.out.println(Thread.currentThread().getId()+"释放了分布式锁");break;}else {System.out.println(Thread.currentThread().getId()+"没有获取到分布式锁");ThreadUtil.safeSleep(1000);}}});}ThreadUtil.safeSleep(100000);}
}