edis的lua脚本
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于 1993 年开发的,该小组成员有:Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo
lua脚本设计目的
其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
lua脚本在redis中的应用
从 Redis 2.6.0 版本开始起;可通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行执行。
在redis中使用lua脚本的好处:
-
支持原子性操作 - Redis会将整个脚本作为一个整体执行(原子性),中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。
-
降低网络开销 - 将多个请求通过脚本的形式一次发送到服务器,减少了网络的时延。
-
脚本复用 - 客户端发送的脚本可支持永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。
lua基本语法
lua变量
局部变量
local a = 5
全局变量
不需要local关键字。
lua
a = 12 -- 全局变量
local b=3
条件语句
if 条件
:
then
end
if 条件
then
else
end
KEYS数组
传递给Lua脚本零到多个键,KEYS[index],注意索引从1开始。
ARGV数组
是传递给脚本的零到多个附加参数, ARGV[index],注意索引从1开始。
redis.call()
用来调用redis命令。
Hello Lua脚本案例
脚本编辑
在resource路径下创建lua/hello.lua文件
传入的redis key为数组,使用KEYS[index]传参,
传入的redis value为数组,使用ARGV[index]传值。
lua
-- key
local k1 = KEYS[1]
local k2 = KEYS[2]
local k3 = KEYS[3]
-- value
local v1 = ARGV[1]
redis.call('set',k1,v1)
redis.call('set',k2,v1)
redis.call('set',k3,v1)
return "OK"
springboot配置
定义一个读取lua脚本的配置类。
java
@Bean
public DefaultRedisScript<String> defaultRedisScript(){
DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(String.class);
defaultRedisScript.setLocation(new ClassPathResource("lua/hello.lua"));
return defaultRedisScript;
}
单元测试
java
@Test
public void luaTest(){}
List<String> keys = new ArrayList<>();
keys.add("money01");
keys.add("money02");
keys.add("money03");
String val1 = "100";
String val2 = "150";
String val3 = "200";
String execute = stringRedisTemplate.opsForValue()
.getOperations()
.execute(defaultRedisScript,
keys,
val1,val2,val3);
System.out.println("lua调用: "+ execute);
}
}
springboot中Lua脚本的创建流程
定义脚本文件方式
创建lua文件
resources/lua/xxx.lua
编辑lua脚本
-
local 本地变量 类似于js var
-
KEYS数组, 从外界传入的key,下标 从1 开始
-
ARGV 数组, 外界命令中传输的值
-
条件 :
luaif 条件 then ... else .... end
-
tonumber():将字符串转换为数值
-
return
定义script对象读取lua脚本
java
@Bean
public DefaultRedisScript<String> defaultRedisScript(){
DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(String.class);
defaultRedisScript.setLocation(new ClassPathResource("lua/hello.lua"));
return defaultRedisScript;
}
执行脚本
java
stringRedisTemplate.opForValue()
.getOpertation()
.execute(
defaultRedisScript,
keys //ArrayList
val1,val2... //值
);
代码方式实现
将脚本文件的内容转换为字符串,如果lua脚本比较小,在不同程序中lua内容比较灵活,聚合性比较高。可以采用此种方式。
java
String script = "local k1 = KEYS[1]\n" +
"local k2 = KEYS[2]\n" +
"local k3 = KEYS[3]\n" +
"local v1 = ARGV[1]\n" +
"local v2 = ARGV[2]\n" +
"local v3 = ARGV[3]\n" +
"\n" +
"redis.call('set',k1,v1)\n" +
"redis.call('set',k2,v2)\n" +
"redis.call('set',k3,v3)\n" +
"\n" +
"return \"OK\"";
String execute = stringRedisTemplate.opsForValue()
.getOperations()
.execute(
new DefaultRedisScript<>(script,String.class),
new ArrayList<String>(){{
add("m1");
add("m2");
add("m3");
}},
10, 20, 30);
System.out.println("lua调用: "+ execute);
String execute = stringRedisTemplate.opsForValue()
.getOperations()
.execute(
new DefaultRedisScript<>("local k1 = KEYS[1] local k2 = KEYS[2] local k3 = KEYS[3] local v1 = ARGV[1] local v2 = ARGV[2] local v3 = ARGV[3] redis.call('set',k1,v1) redis.call('set',k2,v2) redis.call('set',k3,v3) return \"OK\"", String.class),
new ArrayList<String>() {{
add("m1");
add("m2");
add("m3");
}},
"100", "20", "30");
System.out.println("lua调用: " + execute);
lua脚本创建布隆过滤器
shell
127.0.0.1:6379> BF.ADD newFilter bmFilter
(integer) 1
127.0.0.1:6379>
编写lua脚本
在resource下创建lua脚本
lua
local key = KEYS[1]
local val = ARGV[1]
local retVal = tonumber(redis.call('BF.ADD',key,val))
if retVal==1 then
return 1
else
return 0
end
测试
java
@Test
public void testLua() {
List<String> keys = new ArrayList<>();
keys.add("bmFilter");
Long t = (Long) stringRedisTemplate.opsForValue().getOperations()
.execute(redisScript, keys, "admin888");
System.out.println("--->"+t);
}
代码中加入脚本
java
List<String> keys = new ArrayList<>();
keys.add("bmFilter");
String script ="return redis.call('BF.EXISTS',KEYS[1],ARGV[1])";
Long s = stringRedisTemplate.opsForValue().getOperations()
.execute(new DefaultRedisScript<Long>(script,Long.class), keys,username);
log.debug("s--->", s);
下一章我们研究-redis的限流插件redis-cell