ServiceStack Redis,如何将Lua表作为列表返回

7
我正在使用ServiceStack的Redis客户端。我有一个Lua脚本,用多个Redis调用结果填充了一个Lua表。我想以某种方式返回此表。我的想法是使用客户端库中的ExecLuaShaAsList方法,在lua脚本中只需执行“return 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();
    }
1个回答

11

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

编辑:应该支持简单的Lua表/数组。如果没有查看脚本,则不知道发生了什么。

此外,可以查看这个SO 链接以获取有关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

这将返回:

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字典中的方法可以在这里看到:

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

再次感谢。我最终使用了 json :-) - Thomas
更正,应该是ExecLuaShaAsString,而不是ExecLuaShaAsList。 - Thomas
嗨Thomas,很高兴能帮忙,但这不在你的主题标题范围内。请发布一个不同的问题,我猜这个已经回答了。 在新问题中,请确切地说明您想在Lua脚本中实现什么,以及redis数据的情况。如果可能的话,附加一个rdb文件会有所帮助。 为了帮助故障排除/找到性能瓶颈:将一些日志信息写入[redis日志文件:](http://redis.io/commands/eval)->从脚本发出redis日志 - Tw Bert
1
嗨TW,没问题。我解决了。我将我的Azure服务器从小型更改为中型(1.7 Gb RAM到3.5),现在只需要3秒钟。结果证明这是一个RAM问题。之前我使用了94%的RAM,现在是50%。 - Thomas
不用客气,如果你只是使用Redis进行单个查询,那么你做出了正确的选择。祝你完成得顺利;) - Tw Bert
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接