1、什么是Lua
Lua脚本是一个由C语言编写的小巧脚本语言,在所有脚本引擎中,Lua的速度是最快的。Lua的核心代码不过一万多行,因为是C语言编写的,因此Lua可以在几乎所有的操作系统和平台进行编译运行
2、Lua适用场景
1)、辑相对简单,没有复杂的数据交互,访问频次超高的接口实现
2)、 lua适合的是无阻塞的,如果脚本含有文件读写,也快不到哪去
常见搭配:
Nginx + lua 开发高性能web应用,限流、防止sql注入、请求过滤,黑白名单限制等等等。
redis + lua 实现原子操作,避免多线程数据不一致的问题
3、Lua安装以及基本语法
1)Lua安装教程
2)Lua基本语法
学过java的人,看Lua脚本应该也是手到擒来,这里就不展开讲了。
4、Redis中使用Lua
Redis 2.6 版本之后才通过内嵌支持 Lua 环境,在Redis中通过Lua脚本可以实现原子操作,因为Redis服务端会将Lua脚本当作一条命令去执行,同时如果程序中存在多次Redis交互的场景,也可以通过讲这些命令合并写到一个脚本中,这些减少了网络交互次数,也间接的提升了性能,不过也要保证脚本不要太大,过于复杂。
核心命令:EVAL
EVAL script numkeys key [key …] arg [arg …]
- script:lua脚本程序
- numkeys: 用于指定键名参数的个数
- key [key...]:从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
- arg [arg ...]:附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
比如说执行这样一个命令
EVAL "local num = 1; redis.call('set',KEYS[1],ARGV[1] + num) return redis.call('get',KEYS[1])" 1 testKey 10
其中script参数对应的脚本如下
numkeys的参数对应1,表示后面有几个key
key的参数对应testKey,只有一个key
arg参数对应10,也就是这个key对应的value
这条命令的意思就是定义了一个num,然后往里面set进了一个值,KEYS[1] 也就是传入的参数key,ARGV[1]也就是传入的value,然后进行相加,最后脚本返回相加的一个结果为11。
这样就将原本两个命令先set,再get替换成了一个原子命令去执行。
这里再以Redission作为客户端演示一下,代码里面如何去执行:
public static void main(String[] args) throws Exception{
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
config.useSingleServer().setPassword("123456");
RedissonClient client = Redisson.create(config);
RScript rScript = client.getScript();
String script = "local num = 1;" +
"redis.call('set',KEYS[1],ARGV[1] + num);" +
"return redis.call('get',KEYS[1])";
Integer value = rScript.eval(RScript.Mode.READ_WRITE, script, RScript.ReturnType.VALUE, Collections.singletonList("key"), 10);
System.out.println("直接执行脚本:"+value);
}
在调用api的时候,大家可能会发现eval和evalsha这两个api,其实这两个都是用来执行lua脚本的。但是eval命令是直接发送lua脚本的,而evalsha是发送一个之前执行过的lua脚本的,使用evalsha命令可以减少lua脚本网络发送的开销,不过要注意的是,在使用evalsha这个命令时,要先把脚本 预加载到Redis服务器上,例如这样:
String script = "local num = redis.call('get',KEYS[1]);" +
"redis.call('set',KEYS[1],ARGV[1] + num);" +
"return redis.call('get',KEYS[1])";
// 预加载脚本
String shaDigest = rScript.scriptLoad(script);
Integer value2 = rScript.evalSha(RScript.Mode.READ_WRITE, shaDigest, RScript.ReturnType.VALUE, Collections.singletonList("key"), 10);
System.out.println("执行缓存脚本:"+value2);