原子执行

Lua脚本在Redis中是以原子方式执行的,在Redis服务器执行EVAL命令时,在命令执行完毕并向调用者返回结果之前,只会执行当前命令指定的Lua脚本包含的所有逻辑,其它客户端发送的命令将被阻塞,直到EVAL命令执行完毕为止。因此LUA脚本不宜编写一些过于复杂了逻辑,必须尽量保证Lua脚本的效率,否则会影响其它客户端。

基础定义

1
2
--- 局部变量
local age = 18
1
2
# call函数:执行命令错误时会向调用者直接返回一个错误
127.0.0.1:6379> EVAL "return redis.call('GET',KEYS[1])" 1 hello

EVAL命令

Redis中使用EVAL命令来直接执行指定的Lua脚本。

EVAL luascript numkeys key [key ...] arg [arg ...]

  • EVAL 命令的关键字。
  • luascript Lua 脚本。
  • numkeys 指定的Lua脚本需要处理键的数量,其实就是 key数组的长度。
  • key 传递给Lua脚本零到多个键,空格隔开,在Lua 脚本中通过 KEYS[INDEX]来获取对应的值,其中1 <= INDEX <= numkeys。
  • arg是传递给脚本的零到多个附加参数,空格隔开,在Lua脚本中通过ARGV[INDEX]来获取对应的值,其中1 <= INDEX <= numkeys。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 简单示例
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> EVAL "return redis.call('GET',KEYS[1])" 1 hello
"world"
127.0.0.1:6379> EVAL "return redis.call('GET','hello')"
(error) ERR wrong number of arguments for 'eval' command
127.0.0.1:6379> EVAL "return redis.call('GET','hello')" 0
"world"

lua脚本示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//lua脚本,原子操作
key := "cache_key"
userId := 1
coin := 1
expire := time.Now().AddDate(0, 0, 1).Unix()
script := `
local exists = redis.call("EXISTS", KEYS[1])
if (exists==0)
then
    redis.call("HSET", KEYS[1], 0, 0)
    redis.call("EXPIREAT", KEYS[1], ARGV[3])
end
return redis.call("HINCRBY", KEYS[1], ARGV[1], ARGV[2])
`
_, err := db.Redis(db.DefaultRedis).Eval(script, []string{key}, userId, coin, expire).Result()
if err != nil {
    logger.Sugar.Error(err)
}

参考文章

Redis Lua脚本完全入门