【问题标题】:Redis StackExchange LuaScripts with parameters带参数的 Redis StackExchange LuaScripts
【发布时间】:2022-06-17 18:58:18
【问题描述】:

我正在尝试通过 C# StackExchange 库使用以下 Lua 脚本:

private const string LuaScriptToExecute = @"
local current
current = redis.call(""incr"", KEYS[1])
if current == 1 then
    redis.call(""expire"", KEYS[1], KEYS[2])
    return 1
else
    return current
end

每当我“作为字符串”评估脚本时,它可以正常工作

var incrementValue = await Database.ScriptEvaluateAsync(LuaScriptToExecute,
  new RedisKey[] { key, ttlInSeconds });

如果我理解正确的话,每次调用ScriptEvaluateAsync方法时,脚本都会传输到redis服务器,效果不是很好。

为了克服这个问题,我尝试使用“准备好的脚本”方法,通过运行:

_setCounterWithExpiryScript = LuaScript.Prepare(LuaScriptToExecute);
...
...
var incrementValue = await Database.ScriptEvaluateAsync(_setCounterWithExpiryScript,
    new[] { key, ttlInSeconds });

每当我尝试使用这种方法时,都会收到以下错误:

ERR Error running script (call to f_7c891a96328dfc3aca83aa6fb9340674b54c4442): @user_script:3: @user_script: 3: Lua redis() command arguments must be strings or integers

我做错了什么?

使用“准备好的”接收动态参数的 LuaScript 的正确方法是什么?

【问题讨论】:

    标签: redis lua stackexchange.redis


    【解决方案1】:

    如果我查看documentation:不知道。

    如果我查看unit test on github,它看起来真的很容易。

    (顺便说一句,您的 ttlInSeconds 真的是 RedisKey 而不是 RedisValue?您正在通过 KEYS[2] 访问它 - 不应该是 ARGV[1] 吗?无论如何......)

    看来您应该重写脚本以使用命名参数而不是实参:

    private const string LuaScriptToExecute = @"
    local current
    current = redis.call(""incr"", @myKey)
    if current == 1 then
        redis.call(""expire"", @myKey, @ttl)
        return 1
    else
        return current
    end";
    
    // We should load scripts to whole redis cluster. Even when we dont have any. 
    // In that case, there will be only one EndPoint, one iteration etc...
    _myScripts = _redisMultiplexer.GetEndPoints()
      .Select(endpoint => _redisMultiplexer.GetServer(endpoint))
      .Where(server => server != null)
      .Select(server => lua.Load(server))
      .ToArray();
    
    

    然后以匿名类作为参数执行它:

    
    for(var setCounterWithExpiryScript in _myScripts)
    {
      var incrementValue = await Database.ScriptEvaluateAsync(
        setCounterWithExpiryScript,
        new {
          myKey: (RedisKey)key, // or new RedisKey(key) or idk
          ttl: (RedisKey)ttlInSeconds 
        }
      )// .ConfigureAwait(false); // ? ;-)
    
      // when ttlInSeconds is value and not key, just dont cast it to RedisKey
      /*
      var incrementValue = await 
      Database.ScriptEvaluateAsync(
        setCounterWithExpiryScript,
        new { 
          myKey: (RedisKey)key,
          ttl: ttlInSeconds 
        }
      ).ConfigureAwait(false);*/
    }
    

    【讨论】:

      猜你喜欢
      • 2023-01-25
      • 2022-01-12
      • 2017-08-01
      • 2020-02-15
      • 1970-01-01
      • 2017-05-06
      • 2019-04-05
      • 2018-03-22
      • 1970-01-01
      相关资源
      最近更新 更多