Go Redis Lua 脚本
redis.Script
go-redis 支持 Lua 脚本 redis.Script,在 这里 查看使用示例。
在下面的示例中,Lua 脚本使用 GET, SET 实现了 INCRBY
命令:
var incrBy = redis.NewScript(`
local key = KEYS[1]
local change = ARGV[1]
local value = redis.call("GET", key)
if not value then
value = 0
end
value = value + change
redis.call("SET", key, value)
return value
`)
你可以像这样运行脚本:
keys := []string{"my_counter"}
values := []interface{}{+1}
num, err := incrBy.Run(ctx, rdb, keys, values...).Int()
在 go-redis 中,使用 EVALSHA 执行脚本,如果 SHA 不存在,则使用 EVAL。
你可以在 GitHub 中找到上面的示例。更多的例子,你可以参照 redis_rate,它实现了一个漏桶算法的 限流器。
Lua 和 Go 类型
下面是 Lua 和 Go 语言的类型对照表,Lua 的 number 是一个浮点型数字,用于存储整数和浮点数,在 Lua 中不区分整数和浮点数,但 Redis 总是将 Lua 数字转换为舍去小数部分的整数,例如 3.14 变成 3,如果要返回浮点值,将其作为字符串返回并用 Go 解析成 float64。
Lua return | Go interface{} |
---|---|
number (float64) | int64 (舍弃小数) |
string | string |
false | redis.Nil error |
true | int64(1) |
{ok = "status"} | string("status") |
{err = "error message"} | errors.New("error message") |
{"foo", "bar"} | []interface{}{"foo", "bar"} |
{foo = "bar", bar = "baz"} | []interface{}{} (不支持) |
调试 Lua 脚本
调试 Lua 脚本的最简单方法是使用 redis.log
将消息写入 Redis 日志文件或 redis-server
输出的函数:
redis.log(redis.LOG_NOTICE, "key", key, "change", change)
你也可以参照 Redis Lua 脚本调试器
传递多个值
你可以 for
在 Lua 中使用循环来迭代传递的值,例如对数字求和:
local key = KEYS[1]
local sum = redis.call("GET", key)
if not sum then
sum = 0
end
local num_arg = #ARGV
for i = 1, num_arg do
sum = sum + ARGV[i]
end
redis.call("SET", key, sum)
return sum
结果:
sum, err := sum.Run(ctx, rdb, []string{"my_sum"}, 1, 2, 3).Int()
fmt.Println(sum, err)
// Output: 6 nil
循环 continue
Lua 循环中不支持 continue 语法,不过你可以使用嵌套 repeat 循环和 break 语句来模拟它:
local num_arg = #ARGV
for i = 1, num_arg do
repeat
if true then
do break end -- continue
end
until true
end
错误处理
默认情况下,redis.call 函数会引发 Lua 错误并停止服务,如果要捕获错误,需要使用 redis.pcall 返回带有 err 字段的 Lua table:
local result = redis.pcall("rename", "foo", "bar")
if type(result) == 'table' and result.err then
redis.log(redis.LOG_NOTICE, "rename failed", result.err)
end
要返回自定义错误,请使用 Lua table:
return {err = "error message goes here"}