Redis使用lua脚本的好处:
- 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延
- 原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
- 复用。客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑
简单使用
简单的set、get、del
代码
private static String setLua() {
String lua = "return redis.call('set', 'KEYS[1]',ARGV[1]);";
return lua;
}
private static String getLua() {
String lua = "return redis.call('get', 'KEYS[1]');";
return lua;
}
private static String delLua() {
String lua = "return redis.call('del','KEYS[1]');";
return lua;
}
测试:
SingleSharedRedisClientAgent redisClientAgent = new SingleSharedRedisClientAgent();
List<String> keyList = Lists.newArrayList("TEST_KEY");
List<String> valueList = Lists.newArrayList("LUA脚本测试");
Object obj = redisClientAgent.eval(setLua(),keyList,valueList);
System.out.println("SET返回结果:"+obj);
Object obj1 = redisClientAgent.eval(getLua(),keyList,valueList);
System.out.println("GET返回结果:"+obj1);
说明:不建议写成lua脚本文件,在从文件读写出来。建议lua脚本写成lua脚本字符串。使用redis的evel命令来执行lua脚本
eval(script, keys, args);
eval命令有三个参数,第一个script:lua脚本,第二个keys是redis的中的key,第三个args是key的值或者参数
上面执行的结果:
SET返回结果:OK
GET返回结果:LUA脚本测试
DEL返回结果:1
复杂使用
实现一个访问频率控制,某个ip在短时间内频繁访问页面,需要记录并检测出来,就可以通过Lua脚本高效的实现
lua脚本:
private static String testLua() {
String lua = "local times = redis.call('incr',KEYS[1])\n"
+ "if times == 1 then\n"
+ " redis.call('expire',KEYS[1], ARGV[1])\n"
+ "end\n"
+ "if times > tonumber(ARGV[2]) then\n"
+ " return 0\n"
+ "end\n"
+ "return 1";
return lua;
}
脚本的内容的意思是:将访问频率限制为每10秒最多3次,所以在终端中不断的运行此命令会发现当访问频率在10秒内小于或等于3次时返回1,否则返回0
测试:
public static void main(String[] args) {
SingleSharedRedisClientAgent redisClientAgent = new SingleSharedRedisClientAgent();
List<String> keyList = Lists.newArrayList("10.24.19.118");
List<String> valueList = Lists.newArrayList("10", "3");
for (int i = 0; i < 10; i++) {
if (i == 3) {
System.out.println("等待10秒再继续执行......");
run();
}
Object xx = redisClientAgent.eval(testLua(), keyList, valueList);
System.out.println("返回结果:" + xx);
}
}
private static void run() {
try {
Thread.sleep(1000 * 10);//10秒
} catch (InterruptedException e) {
System.out.print("error" + e);
}
}
访问10次,在第4次的时候线程等待10秒再继续执行。在等待十秒的后执行返回结果1,在循环第7次返回0,说明已经访问3次了。
返回结果:1
返回结果:1
返回结果:1
等待10秒再继续执行......
返回结果:1
返回结果:1
返回结果:1
返回结果:0
返回结果:0
返回结果:0
返回结果:0