【问题标题】:ServiceStack Redis, how to return Lua table as ListServiceStack Redis,如何将 Lua 表作为 List 返回
【发布时间】:2014-04-12 19:10:11
【问题描述】:

我正在使用来自 ServiceStack 的 Redis 客户端。我有一个 Lua 脚本,它用多个 Redis 调用的结果填充 Lua 表。我想以某种方式归还这张桌子。我的想法是使用客户端库中的 ExecLuaShaAsList 方法,并在 lua 脚本中执行“返回 myTable”。它不起作用,我总是得到一个空列表。

如何将lua表返回给redis客户端?

这是我在 Redis 客户端中使用的 C# 脚本:

using (var redisClient = GetPooledRedisClient())
{
    var sha1 = redisClient.LoadLuaScript(luaBody);
    List<string> theList = redisClient.ExecLuaShaAsList(sha1);
    int listLength = theList.Count(); //listLength is always 0 for some reason
}

以下答案提示后更新

LuaBody 是这样创建的:

    private string GetLuaScript(List<CatalogItem> categories, List<CatalogItem> products)
    {
        string categoriesToAggregate = string.Join("\",\"", categories.Select(c=>c.Name));
        categoriesToAggregate = "\"" + categoriesToAggregate + "\"";

        string csSearchResult = string.Join("\",\"", products.Select(c => c.Name));
        csSearchResult = "\"" + csSearchResult + "\"";


        StringBuilder sb = new StringBuilder();
        sb.AppendLine("local categoriesToAggregate = {").Append(categoriesToAggregate).Append("}                        ");
        sb.AppendLine("local csSearchResult = {").Append(csSearchResult).Append("}                                      ");
        sb.AppendLine("local result = {}                                                                                ");
        sb.AppendLine();
        sb.AppendLine("for i=1,#categoriesToAggregate do                                                                ");
        sb.AppendLine(" result[categoriesToAggregate[i]] = 0                                                            ");
        sb.AppendLine();
        sb.AppendLine(" for j=1,#csSearchResult do                                                                      ");
        sb.AppendLine("     local fieldValue = redis.call('hget', 'asr:'..csSearchResult[j], categoriesToAggregate[i])  ");
        sb.AppendLine("     if fieldValue then                                                                          ");
        sb.AppendLine("         result[categoriesToAggregate[i]] = result[categoriesToAggregate[i]] + fieldValue        ");
        sb.AppendLine("     end                                                                                         ");
        sb.AppendLine(" end                                                                                             ");
        sb.AppendLine("end                                                                                              ");
        sb.AppendLine();
        sb.AppendLine("return cjson.encode(result)                                                                      ");

        return sb.ToString();
    }

【问题讨论】:

    标签: lua redis lua-table


    【解决方案1】:

    从 Lua 中,您需要返回一个 Lua 数组或 JSON 对象。 'myTable' 听起来像是一个只在 Lua 解释器中有效的句柄。该句柄在调用后直接清理,因此不会传播到客户端。

    编辑:一个简单的 Lua 表/数组应该是supported。不看剧本,不知道发生了什么。

    另请参阅此 SO link,了解有关 Lua 脚本原子性的更多信息。

    希望这会有所帮助,TW

    编辑 OP 后:

    这是 OP 的原始 Lua 脚本:

    local a={}
    for i = 1, 1, 1 do
      a["47700415"] = redis.call('hget', 'asr:47700415', 'MDEngines')
      a["47700415_000"] = redis.call('hget', 'asr:47700415_000', 'MGEngines')
    end
    return a
    

    答案:不能在 Lua 返回值中返回嵌套值。从你的 ServiceStack 函数中可以看出,Lua 脚本返回一个列表,并且列表没有嵌套。

    这里有两个解决方案,一个带有 JSON 的解决方案会产生轻微的开销(但在编程时可能更容易,并且是 nill-safe)。

    a:使用 cjson

    local a={}
    for i = 1, 1, 1 do
      a["47700415"] = redis.call('hget', 'asr:47700415', 'MDEngines')
      a["47700415_000"] = redis.call('hget', 'asr:47700415_000', 'MGEngines')
    end
    return cjson.encode(a)
    

    MsgPack 也是一种非常漂亮紧凑的序列化格式(我们经常使用它),可以这样返回:

    a-alt:使用 cmsgpack

    return cmsgpack.pack(a)
    

    b:使用简单数组

    local a={}
    for i = 1, 1, 1 do
      a[1] = "47700415"
      a[2] = redis.call('hget', 'asr:47700415', 'MDEngines')
      a[3] = "47700415_000"
      a[4] = redis.call('hget', 'asr:47700415_000', 'MGEngines')
    end
    return a
    

    这会返回:

    一个

    tw@srv-flux-02:~$ redis-cli -p 14312 EVAL "$(cat ~/tw_luatest.lua)" 0 0
    "{\"47700415\":\"Hello\",\"47700415_000\":\"World\"}"
    

    b

    tw@srv-flux-02:~$ redis-cli -p 14312 EVAL "$(cat ~/tw_luatest2.lua)" 0 0
    1) "47700415"
    2) "Hello"
    3) "47700415_000"
    4) "World"
    

    如您所见,我在HSET 中放了一些虚拟数据。

    我也可以推荐这个链接,里面有一些不错的信息:intro-to-lua-for-redis-programmers

    可以看到向 ​​Lua dict 添加值的好方法here

    local fkeys = redis.call('sinter', unpack(KEYS))
    local r = {}
    for i, key in ipairs(fkeys) do
      r[#r+1] = redis.call('hgetall',key)
    end
    return r
    

    【讨论】:

    • 您好 TW,我已经更新了我的代码。你知道有什么问题吗?
    • 再次感谢。我最终使用了 json :-)
    • 你好 TW。我在上面更新了如何创建 Lua 脚本。当我尝试使用大量数据(categoriesToAggregate 包含 6 个项目,csSearchResult 包含 84000 个条目)时,它需要很长时间(7.5 分钟)。需要时间的是“redisClient.ExecLuaShaAsList(sha1)”这一行。如果我多次运行它,有时它可能会快得多(几秒钟而不是几分钟)。主要是它很慢。对于少量数据来说总是很快的。我已经检查了服务器上的 CPU 和内存,但它们从未达到最大值。你知道可能是什么原因吗?
    • 更正,应该是 ExecLuaShaAsString,而不是 ExecLuaShaAsList。
    • 嗨,TW,没问题。我解决了。我将我的 Azure 服务器从小型更改为中型(1.7 Gb 内存到 3.5),现在只需要 3 秒。原来这是一个RAM问题。之前我使用了 94% 的内存,现在是 50%。
    猜你喜欢
    • 2014-08-09
    • 2021-01-14
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    • 2018-08-05
    • 1970-01-01
    • 2012-08-29
    • 2017-06-26
    相关资源
    最近更新 更多