Redis Lua脚本调用TS.ADD报错:参数需为字符串或整数
Azure Redis TimeSeries Lua脚本C#执行报错问题解决
问题背景
在启用TimeSeries模块的Azure Redis缓存Enterprise E10实例中,通过redis-cli执行以下Lua脚本可正常运行,多次调用能为key1时间序列添加4个值为1的时间戳:
EVAL "redis.call('TS.ADD', KEYS[1], '*', ARGV[1], 'RETENTION', ARGV[2], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED'); return redis.call('TS.RANGE', KEYS[1], '-', '+')" 1 key1 1 1800000
执行输出:
1) 1) (integer) 1693323424958 2) 1 2) 1) (integer) 1693323485951 2) 1 3) 1) (integer) 1693323528702 2) 1 4) 1) (integer) 1693323531678 2) 1
但在C# .NET控制台应用中调用相同脚本时,抛出错误:
StackExchange.Redis.RedisServerException: 'ERR Error running script (call to f_503fcb14af1e60051a54d3617d5265f753dfe423): @user_script:1: @user_script: 1: Lua redis() command arguments must be strings or integers'
原C#代码
private static readonly TimeSpan RETENTION_TIME = TimeSpan.FromMinutes(30); private const string LUA_SCRIPT = @"redis.call('TS.ADD', KEYS[1], '*', ARGV[1], 'RETENTION', ARGV[2], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED'); return redis.call('TS.RANGE', KEYS[1], '-', '+')"; private static async Task<int> AddTimestampReturnSumAsync(IDatabase db, string key, int retentionTime) { RedisResult result = await db.ScriptEvaluateAsync(LUA_CMD, new RedisKey[] { key }, new RedisValue[] { retentionTime }); Console.WriteLine(JsonSerializer.Serialize(result)); return 0; // a placeholder, I actually want to return a sum of the values }
调用代码:
int sum = await AddTimestampReturnSumAsync(redis.GetDatabase(), "key1", (int)RETENTION_TIME.TotalMilliseconds);
问题原因
- 参数传递不完整:原Lua脚本需要两个ARGV参数(ARGV[1]是要添加的数值1,ARGV[2]是保留时间),但C#代码中仅传递了保留时间,导致脚本执行时
ARGV[1]缺失,Lua解析参数时出现类型错误。 - 脚本变量名笔误:代码中调用
ScriptEvaluateAsync时使用的LUA_CMD未定义,实际定义的脚本变量是LUA_SCRIPT,这会导致编译或运行时错误。
修正后的代码
修正方法与参数传递
private static readonly TimeSpan RETENTION_TIME = TimeSpan.FromMinutes(30); private const string LUA_SCRIPT = @"redis.call('TS.ADD', KEYS[1], '*', ARGV[1], 'RETENTION', ARGV[2], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED'); return redis.call('TS.RANGE', KEYS[1], '-', '+')"; private static async Task<int> AddTimestampReturnSumAsync(IDatabase db, string key, int value, int retentionTime) { // 传递完整的ARGV参数:第一个是要添加的数值,第二个是保留时间 RedisResult result = await db.ScriptEvaluateAsync(LUA_SCRIPT, new RedisKey[] { key }, new RedisValue[] { value, retentionTime }); // 解析TS.RANGE的结果并计算总和 if (result.IsNull) return 0; var entries = (RedisResult[])result; int sum = 0; foreach (var entry in entries) { var parts = (RedisResult[])entry; sum += (int)parts[1]; } return sum; }
修正调用代码
int sum = await AddTimestampReturnSumAsync(redis.GetDatabase(), "key1", 1, (int)RETENTION_TIME.TotalMilliseconds);
说明
- 确保Lua脚本的参数顺序与C#传递的
RedisValue数组顺序完全对应,ARGV[1]对应数组第一个元素,ARGV[2]对应第二个元素。 - 原子性需求:Lua脚本在Redis中是原子执行的,修正后的代码可以保证
TS.ADD和TS.RANGE操作的原子性。
内容的提问来源于stack exchange,提问作者Alexander Farber
相关产品推荐
相关产品推荐

