go-redis 不允许直接传字符串执行 lua 脚本,因其 eval 方法强制要求 *redis.script 类型以自动处理 sha1 缓存、参数序列化和返回值解析;直接传字符串会导致类型错误或 panic。

为什么 EVAL 和 EVALSHA 不能直接传字符串?
go-redis 不提供裸字符串执行 Lua 的接口,是因为它强制要求脚本内容通过 redis.Script 类型封装。这不是为了增加复杂度,而是为了自动缓存 SHA1 值、复用脚本、避免每次传输完整脚本体——尤其在高频调用时能明显降低网络和 Redis 解析开销。
常见错误是试图用 client.Eval(ctx, "return 1", []string{}, nil) 直接传字符串,这会编译失败:类型不匹配,Eval 第二个参数必须是 *redis.Script。
- 必须先用
redis.NewScript("return 1")构造脚本对象 - 脚本对象可复用,建议定义为包级变量,而非每次临时创建
- 如果脚本含换行或复杂逻辑,用反引号包裹多行字符串更安全,Go 会原样保留
Script.Load 和 Script.Eval 的区别在哪?
Script.Load 只向 Redis 预加载脚本并返回其 SHA1 值,不执行;Script.Eval 才真正运行。多数场景下你不需要显式 Load,因为 Eval 内部会自动尝试加载(若未命中缓存)。
但某些部署环境(如 Redis Cluster)要求脚本必须提前加载到所有分片节点,否则 Eval 可能因节点无脚本而失败。这时需主动调用 Load 并检查错误:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
script := redis.NewScript("return #KEYS")
sha, err := script.Load(ctx, client).Result()
if err != nil {
// 处理加载失败,比如集群节点不可达
}
-
Load成功后,后续Eval会优先用EVALSHA协议,更快更轻量 - 若脚本已存在,
Load是幂等操作,不会报错 - 不要在热路径里反复
Load,它本身有网络开销
如何安全传参:KEYS 和 ARGV 怎么对应 Go 变量?
Lua 脚本里的 KEYS[1] 对应 Eval 调用时的第 1 个 key 参数,ARGV[1] 对应第 1 个 arg 参数。go-redis 要求 keys 和 args 分开传,且 keys 必须是 []string,args 可是任意可序列化类型(内部用 JSON 编码)。
典型错误是把 key 当作 arg 传,导致 Lua 报 ERR Error running script (call to f_...): @user_script:1: user_script:1: attempt to concatenate a nil value —— 因为 KEYS[1] 是空的。
- keys 参数只用于涉及 key 的命令(如
GET,DEL),必须是真实存在的 key 名,不能是计算值 - args 适合传数值、字符串、结构体(会被 JSON.Marshal);若传 struct,Lua 收到的是 JSON 字符串,需用
cjson.decode()解析 - Redis 6.2+ 支持
ACL权限控制,确保客户端账号有eval和对应 key 的读写权限
脚本超时或出错时,怎么拿到 Lua 中的 error?
go-redis 将 Lua 的 error("xxx") 映射为 Go 的 *redis.RedisError,其 Error() 方法返回形如 redis: nil response from server: ERR xxx 的字符串。关键是要检查错误类型,而不是只看 err != nil。
res, err := script.Eval(ctx, client, []string{"mykey"}, 123).Result()
if err != nil {
var e *redis.RedisError
if errors.As(err, &e) && strings.HasPrefix(e.Error(), "redis: nil response from server: ERR") {
// 提取原始 Lua error:e.Error()[40:] 或正则提取 "ERR (.*)"
}
}
- 不要依赖
err.Error()的固定格式,不同 go-redis 版本可能微调前缀 - 更健壮的做法是在 Lua 里用
redis.call("ping")触发一个可控错误,测试错误捕获逻辑 - 注意:Lua 脚本中未 catch 的 panic 也会转成
ERR,但堆栈信息不返回给客户端
Load 并验证返回的 SHA 值一致。










