✨✨个人主页:沫洺的主页
📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏
📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏
📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏
💖💖如果文章对你有所帮助请留下三连✨✨
💌EVAL帮助文档
EVAL-Redis命令参考
传参数的格式
其中 "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 是被求值的 Lua 脚本,数字 2 指定了键名参数的数量, key1 和 key2 是键名参数,分别使用 KEYS[1] 和 KEYS[2] 访问,而最后的 argv1 和 argv2 则是附加参数,可以通过 ARGV[1] 和 ARGV[2] 访问它们
- redis.call()
- eval执行scan
源生scan命令
//例如模糊迭代初始游标为0 前缀为'user.'的Key 期望获取5条的命令 scan 0 match user.* count 5
使用eval执行脚本
eval "return redis.call('scan',ARGV[1],'match',KEYS[1],'count',ARGV[2])" 1 user.* 0 5
💌SpringBoot调用LUA脚本
@SpringBootTest public class AppTests_Lua { @Resource(name = "redisTemplate") private ValueOperations<String, User> valueOperations; @Autowired private StringRedisTemplate stringRedisTemplate; @Test void test1() { for (int i =1 ;i<11;i++){ User user = User.builder().name(i+"号张").age(18).build(); valueOperations.set("user."+i,user); } } @Test void test2(){ String lua = "return redis.call('scan',ARGV[1],'match',KEYS[1],'count',ARGV[2])"; RedisScript<List> redisScript = RedisScript.of(lua,List.class); List<String> keys = new ArrayList<>(); keys.add("user.*"); List execute = stringRedisTemplate.execute(redisScript, keys, "0", "5"); } }
完整的迭代过程
@Test void test2() { String lua = "return redis.call('scan',ARGV[1],'match',KEYS[1],'count',ARGV[2])"; RedisScript<List> redisScript = RedisScript.of(lua, List.class); List<String> keys = new ArrayList<>(); keys.add("user.*"); String cursor = "0"; do { List results = stringRedisTemplate.execute(redisScript, keys, cursor, "5"); cursor = (String) results.get(0); List<String> results_keys = (List<String>) results.get(1); System.out.println(cursor); System.out.println(results_keys); } while (!"0".equals(cursor)); }
简单的get两种方式(KEYS提前预编译好了,与ARGV的区别只是效率不同)
@Test void test3() { String lua = "return redis.call('mget',KEYS[1],KEYS[2])"; RedisScript<List> redisScript = RedisScript.of(lua, List.class); List<String> keys = new ArrayList<>(); keys.add("user.1"); keys.add("user.10"); List results = stringRedisTemplate.execute(redisScript, keys); }
@Test void test3() { String lua = "return redis.call('mget',ARGV[1],ARGV[2])"; RedisScript<List> redisScript = RedisScript.of(lua, List.class); List<String> keys = new ArrayList<>(); List results = stringRedisTemplate.execute(redisScript, keys,"user.1","user.10"); }
💌解决库存负数问题
@SpringBootTest class AppTests_DecrBy { @Resource(name = "redisTemplate") private ValueOperations<String, Integer> valueOperations; @Autowired private StringRedisTemplate stringRedisTemplate; //定义一个产品 private final static String productKey = "product01"; @Test void test1() throws InterruptedException { //设置初始化库存5个 valueOperations.set(productKey, 5); //获取线程池 ExecutorService executorService = Executors.newCachedThreadPool(); //模拟开启10个线程 for (int i = 1; i <= 10; i++) { //模拟扣减i个库存量(qty) int finalI = i; //开启线程执行 executorService.execute(() -> { doing(finalI); }); } //防止主线程停止,导致模拟线程不执行 Thread.sleep(60 * 1000); } //lua脚本语言 private static String lua ; static { 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) "); sb.append(" then "); sb.append(" redis.call('decrby',key,qty) "); sb.append(" return -1 "); sb.append(" else "); sb.append(" return tonumber(redis_qty) "); sb.append(" end "); lua = sb.toString(); } private void doing(Integer qty){ RedisScript<Long> redisScript = RedisScript.of(lua,Long.class); List<String> keys = new ArrayList<>(); keys.add(productKey); Long ret = stringRedisTemplate.execute(redisScript, keys,qty.toString()); if(ret.intValue()==-1){ System.out.println(StrUtil.format("{},扣减成功,扣减数量:{}",Thread.currentThread().getName(),qty)); }else{ System.out.println(StrUtil.format("{},库存不足,需求量:{},库存量:{}",Thread.currentThread().getName(),qty,ret)); } } }
核心代码逻辑
//lua脚本语言 //local定义变量的修饰符 //tonumber将字符串转换为数字类型 //redis.call('get',key)获取当前库存量 //redis.call('decrby',key,qty)扣减所需的库存量 //求量小于库存量时扣减成功返回-1,需求量大于库存量时返回所剩库存量 //最后将lua语言转成字符串 private static String lua ; static { 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) "); sb.append(" then "); sb.append(" redis.call('decrby',key,qty) "); sb.append(" return -1 "); sb.append(" else "); sb.append(" return tonumber(redis_qty) "); sb.append(" end "); lua = sb.toString(); }