Go调用Redis Lua脚本需严格遵循参数顺序、类型映射与返回值解包规则:keys和args必须为[]interface{},集群下keys不能为空且需路由正确;应根据错误处理需求选redis.call或redis.pcall;返回值须用type switch安全解析。

Go 调用 Redis Lua 脚本不是“写完脚本传进去就完事”,核心陷阱在参数顺序、类型映射和返回值解包——错一个地方,本地跑得通,集群直接报错或静默失败。
keys 和 args 必须是 []interface{},且元素类型要对
go-redis 的 client.Eval() 签名是 Eval(ctx, script string, keys, args []interface{}),不是 Redis 原生的 EVAL script numkeys key ... arg ...。常见错误包括:
- 把 key 当
string直接传给keys参数(如keys: "mykey"),结果 Lua 里KEYS是空表,KEYS[1]报错 - 传
nil给keys:单机可能不报错,但 Redis Cluster 强制要求至少一个 key,会返回ERR Error running script ... wrong number of arguments -
args里混用int和string没问题,但若传nil,得用interface{}(nil),不能裸写nil
正确写法示例:
script := "return redis.call('GET', KEYS[1])"
result, err := client.Eval(ctx, script, []interface{}{"user:123"}, []interface{}{}).Result()
用 redis.call 还是 redis.pcall?看错误是否可预期
redis.call 遇错即终止并向上抛 RedisError;redis.pcall 捕获错误并返回 {err="..."} 表。选哪个取决于业务逻辑是否允许“降级”:
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 用
redis.call:比如限流脚本里INCR失败(key 类型不对),说明数据异常,应立刻报错,不该继续 - 用
redis.pcall:比如幂等脚本里查HGETALL,key 可能不存在,你希望返回空而不是中断整个脚本 - 注意:
redis.pcall返回的是 Lua 表,Go 侧拿到的是map[string]interface{},需手动判断v["err"] != nil
返回值类型必须显式判断,不能硬转 string
Lua 脚本最后一行决定 Go 侧 cmd.Val() 的类型:数字 → int64,字符串 → string,数组 → []interface{},nil → nil。直接 .Val().(string) 在返回整数时 panic 是高频事故。
安全做法是先检查 err,再用类型 switch:
cmd := client.Eval(ctx, script, keys, args)
if err := cmd.Err(); err != nil {
// 处理连接错误、NOSCRIPT、语法错等
return
}
switch v := cmd.Val().(type) {
case string:
// 处理字符串
case int64:
// 处理整数,如限流返回 0/1
case []interface{}:
// 处理数组,每个元素仍需再判断类型
case nil:
// 脚本明确 return nil,或 key 不存在
}
集群环境 keys 不能为空,哪怕脚本只读时间
Redis Cluster 要求每个 EVAL 请求必须指定至少一个 key 用于 slot 路由。即使你的脚本只调 redis.call("TIME") 不碰任何 key,也得传一个占位 key:
- 错误写法:
client.Eval(ctx, "return redis.call('TIME')", nil, nil)→ Cluster 报错 - 正确写法:
client.Eval(ctx, "return redis.call('TIME')", []interface{}{"__cluster_routing_key__"}, nil) - 更优做法:用
redis.NewScript()预加载,再调script.Eval(),它内部会自动处理EVALSHA+ 占位 key
真正容易被忽略的点是:这个占位 key 不仅要存在,还得确保它落在你目标节点的 slot 范围内——如果用随机字符串,可能路由到错误节点。生产建议固定用类似 "_routing" 这种哈希后稳定的 key。

















