Redis 使用lua脚本最全教程

Redis 使用lua脚本最全教程1 redis 使用 lua 脚本的语法 RedisEval 命令 执行 Lua 脚本 redis127 0 0 1 6379 gt eval return KEYS 1 KEYS 2 ARGV 1 ARGV 2 2key1key2fir key1 2 key2 3 first 4 second 其中 script 参数是一段 Lua5 1 脚本程序 脚本不必 也不应该 定义为一个 Lua 函数 numkeys 用于指定键名

收录于墨的2020~2021开发经验总结

1、redis 使用lua脚本的语法

Redis Eval 命令 – 执行 Lua 脚本

redis 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second" 

可以直接通过 redis-cli –eval执行写好的lua脚本:

redis-cli --eval /test.lua 0 

2、Lua

lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

下载

print('hello world') -- 注释 a=1 b="abc" c={ 
   } d=print c={ 
   "a","b","c"} print(type(a)) print(type(b)) print(type(c)) print(type(d)) -- 多行注释 [[ -------- Output ------ number string table function ]] a="single 'quoted' string and double \"quoted\" string inside" b='single \'quoted\' string and double "quoted" string inside' c= [[ multiple line with 'single' and "double" quoted strings inside.]] print(a) print(b) print(c) [[ -------- Output ------ single 'quoted' string and double "quoted" string inside single 'quoted' string and double "quoted" string inside multiple line with 'single' and "double" quoted strings inside. ]] a,b,c,d,e = 1, 2, "three", "four", 5 a,b,c,d,e = 1, 1.123, 1E9, -123, .0008 print("a="..a, "b="..b, "c="..c, "d="..d, "e="..e) [[ -------- Output ------ a=1 b=1.123 c= d=-123 e=0.0008 ]] address={ 
   } -- empty address address.Street="Wyman Street" address.StreetNumber=360 address.AptNumber="2a" address.City="Watertown" address.State="Vermont" address.Country="USA" print(address.StreetNumber, address["AptNumber"]) -- end 结束 a=1 if a==1 then print ("a is one") end c=3 if c==1 then print("c is 1") elseif c==2 then print("c is 2") else print("c isn't 1 or 2, c is "..tostring(c)) end a=1 b=(a==1) and "one" or "not one" print(b) -- b = ((a==1) ? "one" : "not one") -- 循环 a=1 while a~=5 do -- Lua uses ~= to mean not equal a=a+1 io.write(a.." ") end a=0 repeat a=a+1 print(a) until a==5 for a=1,6,3 do io.write(a) end -- 14 [[ for (int i = 1; i < 6; i += 3) { printf(i); } ]] for key,value in pairs({ 
   1,2,3,4}) do print(key, value) end [[ -------- Output ------ 1 1 2 2 3 3 4 4 ]] a={ 
   1,2,3,4,"five","elephant", "mouse"} for i,v in pairs(a) do print(i,v) end [[ -------- Output ------ 1 1 2 2 3 3 4 4 5 five 6 elephant 7 mouse ]] -- break a=0 while true do a=a+1 if a==10 then break end end -- 函数 function myFirstLuaFunctionWithMultipleReturnValues(a,b,c) return a,b,c,"My first lua function with multiple return values", 1, true end a,b,c,d,e,f = myFirstLuaFunctionWithMultipleReturnValues(1,2,"three") print(a,b,c,d,e,f) [[ -------- Output ------ 1 2 three My first lua function with multiple return values 1 true ]] -- local 局部变量 function myfunc() local b=" local variable" a="global variable" print(a,b) end function printf(fmt, ...) io.write(string.format(fmt, ...)) end printf("Hello %s from %s on %s\n", os.getenv"USER" or "there", _VERSION, os.date()) -- Math functions: -- math.abs, math.acos, math.asin, math.atan, math.atan2, -- math.ceil, math.cos, math.cosh, math.deg, math.exp, math.floor, -- math.fmod, math.frexp, math.huge, math.ldexp, math.log, math.log10, -- math.max, math.min, math.modf, math.pi, math.pow, math.rad, -- math.random, math.randomseed, math.sin, math.sinh, math.sqrt, -- math.tan, math.tanh -- String functions: -- string.byte, string.char, string.dump, string.find, string.format, -- string.gfind, string.gsub, string.len, string.lower, string.match, -- string.rep, string.reverse, string.sub, string.upper -- Table functions: -- table.concat, table.insert, table.maxn, table.remove, table.sort -- IO functions: -- io.close , io.flush, io.input, io.lines, io.open, io.output, io.popen, -- io.read, io.stderr, io.stdin, io.stdout, io.tmpfile, io.type, io.write, -- file:close, file:flush, file:lines ,file:read, -- file:seek, file:setvbuf, file:write print(io.open("file doesn't exist", "r")) -- OS functions: -- os.clock, os.date, os.difftime, os.execute, os.exit, os.getenv, -- os.remove, os.rename, os.setlocale, os.time, os.tmpname -- require导入包 require( "iuplua" ) ml = iup.multiline { 
    expand="YES", value="Quit this multiline edit app to continue Tutorial!", border="YES" } dlg = iup.dialog{ 
   ml; title="IupMultiline", size="QUARTERxQUARTER",} dlg:show() print("Exit GUI app to continue!") iup.MainLoop() 

Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。

Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。 这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。

3、redis使用Lua

通过return 返回结果,通过redis.call执行redis命令:

eval "return redis.call('keys','*')" 0 

以上命令返回所有的key,类似于直接执行 keys *

以下命令删除dict*格式的所有key值

eval "local redisKeys = redis.call('keys',KEYS[1]..'*');for i,k in pairs(redisKeys) do redis.call('del',k);end;return redisKeys;" 1 dict 

展开如下

local redisKeys = redis.call('keys',KEYS[1]..'*'); for i,k in pairs(redisKeys) do redis.call('del',k); end; return redisKeys; 

以下命令删除所有key值

eval "local sum = 0;for i,k in pairs(redis.call('keys','*')) do redis.call('del', k);sum=sum+1;end; return 'clear '..sum..' key'" 0 

批量生产key值,设置过期时间,参数: 2、 key个数、 key前缀、 key的值、 key的过期时间(可选)

 eval "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]..'*');" 2 10 test 0 20 

在这里插入图片描述

删除所有值为0的key,参数:0、值X

 eval "local ks = {};for i,k in pairs(redis.call('keys','*')) do local v = redis.call('get',k);if v==ARGV[1] then redis.call('del',k);table.insert(ks,k); end;end;return ks;" 0 0 

删除所有永不过期的key

 eval "local ks = {};for i,k in pairs(redis.call('keys','*')) do local ttl = redis.call('ttl',k);if ttl==-1 then redis.call('del',k);table.insert(ks,k); end;end;return ks;" 0 

获取所有值为0,并以test为前缀的key列表,参数:2、x、y

 eval "local ks = {};for i,k in pairs(redis.call('keys',KEYS[1]..'*')) do local v = redis.call('get',k);if v==ARGV[1] then table.insert(ks,k); end;end;return ks;" 1 test 0 

redis分布式锁实现,之加锁。如果不存在lock,则设置local233,并设置过期时间为60,如果返回1表示加锁成功,返回0则加锁失败,该操作是原子操作,可以由等效命令 set lock 233 nx ex 60代替:

eval "if redis.call('get',KEYS[1]) then return 0;else redis.call('set',KEYS[1],ARGV[1]);redis.call('expire',KEYS[1],ARGV[2]);return 1;end;" 1 lock 233 60 

展开如下

if redis.call('get',KEYS[1]) then return 0; else redis.call('set',KEYS[1],ARGV[1]); redis.call('expire',KEYS[1],ARGV[2]); return 1; end 

redis分布式锁实现,之释放锁。如果不存在lock,则无需释放,如果存在lock并且值和传入的值一致,那么删除lock,释放成功,其他情况返回释放失败。成功:1,失败0。

eval "local v = redis.call('get',KEYS[1]);if v then if v~=ARGV[1] then return 0;end;redis.call('del',KEYS[1]);end;return 1;" 1 lock 233 

展开如下

local v = redis.call('get',KEYS[1]); if v then -- 如果和传入的值不同,返回0表示失败 if v~=ARGV[1] then return 0; end; -- 删除key redis.call('del',KEYS[1]); end; return 1; 

4、redisTemplate执行脚本的方法封装

 @Component public class RedisUtil { 
    @Resource private RedisTemplate<String, Object> redisTemplate; / * 执行 lua 脚本 * @author hengyumo * @since 2021-06-05 * * @param luaScript lua 脚本 * @param returnType 返回的结构类型 * @param keys KEYS * @param argv ARGV * @param <T> 泛型 * * @return 执行的结果 */ public <T> T executeLuaScript(String luaScript, Class<T> returnType, String[] keys, String... argv) { 
    return redisTemplate.execute(RedisScript.of(luaScript, returnType), new StringRedisSerializer(), new GenericToStringSerializer<>(returnType), Arrays.asList(keys), (Object[])argv); } } 

使用很简单,以下用上边使用过的两个脚本作为示例:

 @Resource private RedisUtil redisUtil; @Test @SuppressWarnings("unchecked") public void testExecuteLuaScript() { 
    String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"; List<Object> list = (List<Object>)redisUtil.executeLuaScript(script, List.class, new String[] { 
   "a", "b"}, "a", "b"); list.forEach(x -> System.out.println(x.toString())); script = "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 = (List<Object>)redisUtil.executeLuaScript(script, List.class, new String[] { 
   "10", "test"}, "0", "60"); list.forEach(x -> System.out.println(x.toString())); } 

输出结果,返回的结果是List<List>:

[a] [b] [a] [b] [test1] [test10] [test2] [test3] [test4] [test5] [test6] [test7] [test8] [test9] 

封装方法:删除以key为前缀的所有键值

 // 以下命令删除xxx*格式的所有key值 private final static String LUA_SCRIPT_CLEAR_WITH_KEY_PRE = "local redisKeys = redis.call('keys',KEYS[1]..'*');" + "for i,k in pairs(redisKeys) do redis.call('del',k);end;" + "return redisKeys;"; / * @author hengyumo * @since 2021-06-05 * * 删除以key为前缀的所有键值 * @param keyPre 前缀 * @return 返回删除掉的所有key */ public List<String> deleteKeysWithPre(String keyPre) { 
    @SuppressWarnings("unchecked") List<Object> result = executeLuaScript(LUA_SCRIPT_CLEAR_WITH_KEY_PRE, 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()); } 

使用很简单:

 @Test public void testDeleteKeysWithPre() { 
    List<String> list = redisUtil.deleteKeysWithPre("DAWN"); list.forEach(System.out::println); } 

END

写作不易,您的小小一个?会让我更加有动力。


那在终点之前,我愿意再爱一遍。 ——墨

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231615.html原文链接:https://javaforall.net

(0)
上一篇 2025年12月1日 下午4:01
下一篇 2025年12月1日 下午4:22


相关推荐

  • CANoe简易教程2

    CANoe简易教程2转载自 http blog michiru me posts canoe tutorial part 2 html 在这份教程的第一部分 我们提到没有 CAN 数据库 CANoe 还不能工作 在第二部分 我们来看看 CAN 数据库是怎样的 通过 CANoe 的 Tools 菜单 或者从开始菜单打开 Vector 的 CAN 数据库编辑器 CANdb Editor 简介 CAN 总线上有 4 种报文

    2026年3月18日
    3
  • jsonpath 判断是否包含_JSONPath介绍

    jsonpath 判断是否包含_JSONPath介绍/***@authoritguang*@create2017-12-1010:03**/@RunWith(SpringRunner.class)@SpringBootTest@Slf4jpublicclassJSONpathControllerTest{@Testpublicvoidtest(){Useruser=newUser(“itguang”,”123456″…

    2022年6月22日
    103
  • 什么是C语言数组地址

    什么是C语言数组地址还记得以前有和同事聊过C语言数组这个概念,那时候大家都还不是掌握的很好,总会搞错数组的地址。但是总有人会对数组的地址这个概念产生怨念,他们认为一个数组a本身就是地址,殊不知数组名a只是其首元素的地址,而&a才是数组a的地址。拓展:假设有一个数据inta[5];那么,a代表的是a[0]的地址,换句话说,a等价于&a[0],假如这个地址值是0x123,那么a+1的值是0…

    2022年7月22日
    14
  • js逗号隔开的字符串转数组_字符串匹配逗号组成数组

    js逗号隔开的字符串转数组_字符串匹配逗号组成数组varplateform_id_array=[1,2];varplateform_id_str=plateform_id_array.join(‘,’);

    2025年8月4日
    5
  • 计算机考研各省份学校,想考研究生,哪个省份的高校更容易考上?

    计算机考研各省份学校,想考研究生,哪个省份的高校更容易考上?广东省2020年报考人数为17.4万,比去年增长24.3%。考研人数在过去的5年里飞速增长,自2015年的5.16万到2020年的17.4万,五年时间里翻了两倍多,涨幅在国内名列前茅。山东省2020年硕士研究生招生考试准考人数共313190人,比去年增加58704人,增幅为23.1%,同样每年新增几万的考研人。江苏省2020年共有24.9万名考生报名参加硕士研究生考试,比去年增长17.7%,再创历…

    2022年5月22日
    76
  • PyTorch建立resnet34和resnet101代码[通俗易懂]

    PyTorch建立resnet34和resnet101代码[通俗易懂]model.pyimporttorch.nnasnnimporttorchclassBasicBlock(nn.Module):expansion=1def__init__(self,in_channel,out_channel,stride=1,downsample=None):super(BasicBlock,self).__init__()self.conv1=nn.Conv2d(in_channels=

    2026年4月16日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号