收录于墨的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,则设置local为233,并设置过期时间为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
