# LUA脚本 Redis 通过 LUA 脚本创建具有原子性的命令: 当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。 在Redis中执行Lua脚本有两种方法:eval和evalsha。 eval 命令使用内置的 Lua 解释器,对 Lua 脚本进行求值。 ``` //第一个参数是lua脚本,第二个参数是键名参数个数,剩下的是键名参数和附加参数 > eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second" ``` ## evalsha Redis还提供了evalsha命令来执行Lua脚本。首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和。Evalsha 命令根据给定的 sha1 校验和,执行缓存在服务器中的脚本。 script load命令可以将脚本内容加载到Redis内存中。 ``` redis 127.0.0.1:6379> SCRIPT LOAD "return 'hello moto'" "232fd51614574cf0867b83d384a5e898cfd24e5a" redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0 "hello moto" ``` 使用evalsha执行Lua脚本过程如下: ![](http://img.topjavaer.cn/img/evalsha.png) ## lua脚本作用 1、Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。 2、Lua脚本可以将多条命令一次性打包,有效地减少网络开销。 ## 应用场景 限制接口访问频率。 在Redis维护一个接口访问次数的键值对,key是接口名称,value是访问次数。每次访问接口时,会执行以下操作: - 通过aop拦截接口的请求,对接口请求进行计数,每次进来一个请求,相应的接口count加1,存入redis。 - 如果是第一次请求,则会设置count=1,并设置过期时间。因为这里set()和expire()组合操作不是原子操作,所以引入lua脚本,实现原子操作,避免并发访问问题。 - 如果给定时间范围内超过最大访问次数,则会抛出异常。 ```java private String buildLuaScript() { return "local c" + "\nc = redis.call('get',KEYS[1])" + "\nif c and tonumber(c) > tonumber(ARGV[1]) then" + "\nreturn c;" + "\nend" + "\nc = redis.call('incr',KEYS[1])" + "\nif tonumber(c) == 1 then" + "\nredis.call('expire',KEYS[1],ARGV[2])" + "\nend" + "\nreturn c;"; } String luaScript = buildLuaScript(); RedisScript redisScript = new DefaultRedisScript<>(luaScript, Number.class); Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period()); ```