StackExchange.Redis预编译Lua脚本传参报错问题咨询
问题根因
报错的核心原因是混淆了StackExchange.Redis预编译Lua脚本的两类参数边界:
- Redis执行Lua脚本时本身就明确区分
KEYS(键参数)和ARGV(普通值参数):KEYS用于传递所有涉及的Redis键,方便集群节点做路由校验;ARGV用于传递过期时间、运算值这类普通业务参数。 - 直接传入原始字符串脚本调用
ScriptEvaluateAsync时,库不会做严格的参数语义校验,你把TTL过期值塞到KEYS数组中,最终传输时也会被序列化为合法的整数/字符串,所以能正常运行,但这种写法本身不符合Redis Lua脚本的参数规范,集群环境下还可能触发路由错误。 - 使用
LuaScript.Prepare预编译脚本时,库会严格校验参数类型:所有传入KEYS集合的参数会被标记为Redis键做特殊处理,你把TTL这个普通整数值放到KEYS位置,最终传递给Lua运行时的参数不是合法的字符串/整数类型,就抛出了参数类型错误。
预编译Lua脚本的正确用法
1. 修正Lua脚本的参数引用
把普通值参数的引用从KEYS改为ARGV:
private const string LuaScriptToExecute = @" local current current = redis.call(""incr"", KEYS[1]) if current == 1 then redis.call(""expire"", KEYS[1], ARGV[1]) return 1 else return current end ";
2. 预编译脚本
在服务初始化阶段执行一次预编译即可,不需要每次执行都重新编译:
private static readonly LuaScript _setCounterWithExpiryScript = LuaScript.Prepare(LuaScriptToExecute);
3. 执行脚本时区分两类参数
调用执行时,显式分开传递键参数和普通值参数,两种写法都可以:
- 写法1:显式指定keys和values数组,逻辑最清晰
var incrementValue = await Database.ScriptEvaluateAsync( _setCounterWithExpiryScript, keys: new RedisKey[] { key }, values: new RedisValue[] { ttlInSeconds } );
- 写法2:传入匿名对象,库会自动按字段名映射,所有带
key前缀的字段会被识别为KEYS参数,其余字段识别为ARGV参数
var incrementValue = await Database.ScriptEvaluateAsync(_setCounterWithExpiryScript, new { key = key, ttl = ttlInSeconds });
预编译脚本第一次执行时会自动将脚本内容上传到Redis服务端并缓存SHA1值,后续执行只会传递SHA1值和参数,不会重复传输完整脚本内容,能有效降低网络传输开销。
内容的提问来源于stack exchange,提问作者Ilia
相关产品推荐
相关产品推荐

