Java+redis+lua 实现在用lua脚本执行Redis相关的操作
1.将lua脚本的内容加载出来放入到DefaultRedisScript
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.scripting.support.ResourceScriptSource; @Configuration public class LuaConfig { /** * 将lua脚本的内容加载出来放入到DefaultRedisScript * * @return */ @Bean public DefaultRedisScript<String> getLua() { DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>(); defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/get.lua"))); defaultRedisScript.setResultType(String.class); return defaultRedisScript; } }
2. RedisManager 操作类
/** * RedisManager 操作类 * 使用SpringDataRedis进行整合 * * @author yy * @since 1.0.0, 2019年08月20日 */ @Component public class RedisManager { @Resource private StringRedisTemplate stringRedisTemplate; public Object lua(RedisScript redisScript, List<String> keys, Long defaultExpire) { RedisSerializer<String> stringRedisSerializer = stringRedisTemplate.getStringSerializer(); Object result = stringRedisTemplate.execute(redisScript, stringRedisSerializer, stringRedisSerializer, keys, String.valueOf(defaultExpire)); System.out.println(result); return result; } /** * 执行 lua 脚本 * @author hengyumo * @since 2021-06-05 * * @param luaScript lua 脚本 * @param returnType 返回的结构类型 * @param keys KEYS * @param argv ARGV * @param <T> 泛型 * * @return 执行的结果 */ public <T> Object executeLuaScript(String luaScript, Class<T> returnType, String[] keys, String... argv) { return stringRedisTemplate.execute(RedisScript.of(luaScript, returnType), new StringRedisSerializer(), new GenericToStringSerializer<>(returnType), Arrays.asList(keys), (Object[])argv); } /** * @author hengyumo * @since 2021-06-05 * * 删除以key为前缀的所有键值 * @param keyPre 前缀 * @return 返回删除掉的所有key */ public List<String> deleteKeysWithPre(String luaScript,String keyPre) { @SuppressWarnings("unchecked") List<Object> result = (List<Object>) executeLuaScript(luaScript, List.class, new String[] {keyPre}); return result.stream().map(x -> { if (x instanceof List) { @SuppressWarnings("unchecked") List<String> list = (List<String>) x; if (list.size() > 0) { return list.get(0); } } return null; }).filter(Objects::nonNull).collect(Collectors.toList()); } /** * 执行lua 脚本 * @param defaultRedisScript * @param keyList key 值集合 * @return */ public Object execute(DefaultRedisScript<String> defaultRedisScript, List<String> keyList) { return stringRedisTemplate.execute(defaultRedisScript, keyList); } /** * 执行lua 脚本 * @param longDefaultRedisScript * @param asList key 值集合 * @param argv vlue 的判断条件 * @param <T> * @return */ public <T> Object executeLong(DefaultRedisScript<Long> longDefaultRedisScript, List<String> asList, Object[] argv) { return stringRedisTemplate.execute(longDefaultRedisScript, asList, argv); } /** * 执行lua 脚本 * @param longDefaultRedisScript * @param asList key 值集合 * @param argv vlue 的判断条件 * @param <T> * @return */ public <T> Object execute(DefaultRedisScript<String> longDefaultRedisScript, List<String> asList, Object[] argv) { return stringRedisTemplate.execute(longDefaultRedisScript, asList, argv); } /** * 执行lua 脚本 * @param longDefaultRedisScript * @param asList key 值集合 * @param value1 vlue 的第一个参数判断条件 * @param value2 vlue 的第二个参数判断条件 * @param <T> * @return */ public <T> Object execute(DefaultRedisScript<Long> longDefaultRedisScript, List<String> asList, String value1, String value2) { return stringRedisTemplate.execute(longDefaultRedisScript, asList, value1,value2); } }
3.测试lua执行redis的语句
@Slf4j @SpringBootTest @RunWith(SpringRunner.class) public class RedisTestControllerTest { @Autowired private RedisManager redisTemplate; /** * 获取注入的lua脚本文件 */ @Autowired private DefaultRedisScript<String> getLua; @Test public void testExecuteLuaScript() { String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"; List<Object> list = (List<Object>) redisTemplate.executeLuaScript(script, List.class, new String[]{"a", "b"}, "a", "b"); list.forEach(x -> System.out.println(x.toString())); String script2 = "for i=1,KEYS[1],1 do local k=KEYS[2]..i; redis.call('set',k,ARGV[1]);" + "if ARGV[2] then redis.call('expire',k,ARGV[2]) end;end;" + "return redis.call('keys',KEYS[2]..'*');"; List<Object> list2 = (List<Object>) redisTemplate.executeLuaScript(script2, List.class, new String[]{"10", "test"}, "0", "60"); list2.forEach(x -> System.out.println(x.toString())); } @Test public void redisLuasetnx() { String script1 = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('set', KEYS[2], ARGV[2]) return 1 else return 0 end"; Long result1 = (Long) redisTemplate.execute( new DefaultRedisScript<Long>(script1, Long.class), Arrays.asList("key3", "key4"), "value1", "value2" ); System.out.println(result1); // 1 } @Test public void redisLuaget() { // 如果key1==value1,则删除key1,返回删除的状态,否则返回0 String script2 = "if redis.call('get', KEYS[1]) == tonumber(ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end"; Long result2 = (Long) redisTemplate.executeLong( new DefaultRedisScript<Long>(script2, Long.class), Arrays.asList("key3"), new Object[]{"2"} ); System.out.println(result2); // 1 } /** * redis + lua + set 存数据 */ @Test public void redisLuaset() { String script1 = "return redis.call('set', KEYS[1],'bar')"; String result1 = (String) redisTemplate.execute( new DefaultRedisScript<String>(script1, String.class), Arrays.asList("key_bar") ); System.out.println(result1); // 1 } /** * redis + lua + set 存数据 */ @Test public void redisLuasetvalue() { String script1 = "return redis.call('set', KEYS[1],ARGV[1])"; String result1 = (String) redisTemplate.execute( new DefaultRedisScript<String>(script1, String.class), Arrays.asList("key_bar2"), new Object[]{"2"} ); System.out.println(result1); // 1 } /** * redis + lua + get 取数据 */ @Test public void redisLuaGet1() { String script1 = "return redis.call('get', KEYS[1])"; Object result1 = redisTemplate.execute( new DefaultRedisScript<>(script1, String.class), Collections.singletonList("key_bar") ); System.out.println(result1); // 1 } /** * redis + lua + set 存数据 加过期时间 */ @Test public void redisLuaSetTime() { // String script0 = "return redis.call('set', KEYS[1],'bar', 'nx', 'ex', 10)"; String script1 = "return redis.call('set', KEYS[1],'bar', 'nx', 'ex', ARGV[1])"; Long result1 = (Long) redisTemplate.executeLong( new DefaultRedisScript<>(script1, Long.class), Collections.singletonList("key_time1"), new Object[]{"130"} ); System.out.println(result1); // 1 } /** * redis + lua + set 存数据 加过期时间 */ @Test public void redisLuaSetTime2() { // String script0 = "return redis.call('set', KEYS[1],'bar', 'nx', 'ex', 10)"; String script1 = "return redis.call('set', KEYS[1],ARGV[1], 'nx', 'ex', ARGV[2])"; String result1 = (String) redisTemplate.execute( new DefaultRedisScript<>(script1, String.class), Collections.singletonList("key_time1"), new Object[]{"130","60"} ); System.out.println(result1); // 1 } /** * 以下命令删除xxx*格式的所有key值 */ @Test public void testDeleteKeysWithPre() { // 以下命令删除xxx*格式的所有key值 String luaScript = "local redisKeys = redis.call('keys',KEYS[1]..'*');" + "for i,k in pairs(redisKeys) do redis.call('del',k);end;" + "return redisKeys;"; List<String> list = redisTemplate.deleteKeysWithPre(luaScript, "DAWN"); list.forEach(System.out::println); } /** * 测试并发,多人同时在一时间取数据 * @throws Exception */ @Test public void lpushbuy1() throws Exception { // 请求总数 int clientTotal = 5000; // 同时并发执行的线程数 int threadTotal = 200; // Random rd=new Random(); ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal; i++) { executorService.execute(() -> { try { semaphore.acquire(); Long userId = Math.round(Math.random() * 100000000); Long goodsId = 1L; lpushbuy(goodsId, userId); } catch (InterruptedException e) { e.printStackTrace(); } semaphore.release(); countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); } /** * 秒杀功能 * @param goodsId * @param userId */ public void lpushbuy(Long goodsId, Long userId) { String script0 = " local u = redis.call ('get', KEYS[1]);if u then return 2; else local v = redis.call ('lpop', KEYS[2]);if v then redis.call ('set', KEYS[1], ARGV[1]); return 1 else return 0; end; end; "; String script1 = " local u = redis.call ('get', KEYS[1]);if u then return 2; else local v = redis.call ('lpop', KEYS[2]);if v then redis.call ('set', KEYS[1], ARGV[1], 'nx', 'ex', ARGV[2]); return 1 else return 0; end; end; "; Long result1 = (Long) redisTemplate.execute( new DefaultRedisScript<>(script1, String.class), Arrays.asList("user_x:" + userId, "goodsId_x:" + goodsId), new Object[]{String.valueOf(goodsId), "100"}); System.out.println("result1!!!!!!!!!!!!!!!!!!!!!! =========" + result1); if (result1 == 0) { System.out.println("结束"); } else if (result1 == 1) { System.out.println("成功"); } else if (result1 == 2) { System.out.println("已经获取到了不逼重复获取"); } else { System.out.println("失败"); } } @Test public void lpushbuy2() { Long userId = Math.round(Math.random() * 100000000); Long goodsId = 1L; lpushbuy(goodsId, userId); } /** * 测试lua脚本写在文件里面 */ @Test public void getlua() { String redisKey = "aaa"; Object result1 = redisTemplate.execute( getLua, Collections.singletonList(redisKey)); System.out.println(result1.toString()); } }
4.lua文件
在项目的resources 文件下面建立一个get.lua文件,里面写入一下内容
return redis.call('get', KEYS[1])
完成
本文来自博客园,作者:黄橙,转载请注明原文链接:https://www.cnblogs.com/RedOrange/p/17095549.html
分类:
lua
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!