【问题标题】:Retrieving and deserializing large lists of Redis-cache items检索和反序列化大型 Redis 缓存项列表
【发布时间】:2020-09-27 07:30:08
【问题描述】:

我们计划在现有解决方案中添加 Redis 缓存。

我们有这个核心实体,它被大量提取,每个会话几次。该实体由 13 列组成,其中大部分少于 20 个字符。通常它由父 id 检索,但有时作为由 id 列表获取的子集。为了解决这个问题,我们正在考虑实施下面的解决方案,但问题是这是否是个好主意?该列表通常包含 400 项左右,但在某些情况下可能多达 3000 项。

我们将使用以下键模式将实例存储在列表中:EntityName:{ParentId}:{ChildId},其中ParentIdChildId 是整数。

然后要根据ParentId 检索列表,我们将调用以下方法,并将EntityName:{ParentId}:* 作为模式参数的值:

public async Task<List<T>> GetMatches<T>(string pattern)
{
    var keys = _multiPlexer.GetServer(_multiPlexer.GetEndPoints(true)[0]).Keys(pattern: pattern).ToArray();
    var values = await Db.StringGetAsync(keys: keys);

    var result = new List<T>();
    foreach (var value in values.Where(x => x.HasValue))
    {
        result.Add(JsonSerializer.Deserialize<T>(value));
    }

    return result;
}

为了检索特定的项目列表,我们将调用以下方法并使用精确键列表:

public async Task<List<T>> GetList<T>(string[] keys)
{
    var values = await Db.StringGetAsync(keys: keys.Select(x => (RedisKey)x).ToArray());

    var result = new List<T>();
    foreach (var value in values.Where(x => x.HasValue))
    {
        result.Add(JsonSerializer.Deserialize<T>(value));
    }

    return result;
}

这里明显的担忧是要反序列化的对象数量和System.Text.Json 的性能。

另一种方法是将数据存储两次,作为列表和单独存储,但这只会在我们通过ParentId 获取的情况下有所帮助。我们也可以只将数据存储为列表并每次检索它,但有时只使用一个子集。

有没有更好的方法来解决这个问题?

非常感谢所有输入!谢谢!


编辑

我编写了一个小型控制台应用程序来对备选方案进行负载测试,使用模式匹配 100 次获取 2000 个项目需要 2020 毫秒,获取列表需要 1568 毫秒。我认为我们可以接受这种差异并使用模式匹配。

【问题讨论】:

  • 由于 Redis 只是一个简单的键值对存储而不是搜索数据库,因此您能做的最好的事情就是使用针对您希望的查找方式进行优化的不同键复制数据。或者,您应该研究您的应用程序是否可以采用不同的结构;如果您可以避免查找列表,我认为这是可取的。
  • 感谢您的回答!当然,我意识到这是首选方式,也许我们可以重构,这样就可以了。但是如果我们用这个解决方案去哪里,有问题的部分是反序列化还是redis mget?
  • 不幸的是,您必须进行一些性能分析才能弄清楚这一点。这在很大程度上取决于您用于 Redis 服务器和 JSON 序列化程序的设置和设置以及对象的大小。但是 JSON 反序列化的替代方法是什么?您总是需要某种方式来反序列化进程之间传输的数据。
  • 当然,我曾考虑将 protobuf 作为 json 的替代品,但是是的,它并没有真正改变事实。我将进行一些分析以查看额外的 500 毫秒在哪里,并进行一些压力测试以查看对服务器的影响。谢谢!

标签: c# stackexchange.redis system.text.json


【解决方案1】:

看来@Xerillio 是对的。我使用托管服务进行了一些负载测试,然后使用模式匹配获取列表几乎慢了三倍,比直接从 SQL 接收列表慢。所以,要回答我自己的问题,如果这是一个好主意,我会说不,不是。增加的大部分时间不是因为反序列化,而是因为使用模式匹配获取键。

这是在一个循环中获取 2000 个项目 100 个项目的结果:

  • 直接从 db = 8625ms 获取
  • 使用精确键列表获取 = 5663 毫秒
  • 使用 match = 13098ms 获取
  • 获取完整列表 = 5352 毫秒

【讨论】:

  • 您可能会尝试使用 mget 或 hget 而不是循环中的 100 个项目...只需构建密钥数组和 mget。根据您的时间安排 - mget 将在 1500 毫秒范围内。
猜你喜欢
  • 2015-08-28
  • 2015-02-16
  • 2020-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-03
  • 2018-08-05
  • 1970-01-01
相关资源
最近更新 更多