【问题标题】:Caching sortable/filterable data in Redis在 Redis 中缓存可排序/可过滤的数据
【发布时间】:2013-05-14 08:04:17
【问题描述】:

我在标准 Redis 哈希映射中缓存了各种数据,并且遇到了需要响应客户端请求以进行排序和过滤的情况。姓名、平均评分和评论数量的订单排名可以定期更改(可能每分钟多次)。谁能建议我解决这个问题的正确策略?请考虑以下示例以帮助理解我在寻找什么:

  1. 客户端向 /api/v1/cookbooks?orderBy=name&limit=20&offset=0 发出 API 请求
  2. 我应该回复前 20 个条目,按名称排序

到目前为止我考虑过的策略:

  • 对于每种类型的 hashmap 存储(食谱、食谱等),从 Postgres ORDER BY 中为每个排序方案(字母顺序、平均评分等)创建一个排序集;然后根据limit和offset拉出ZRANGE切片
  • 将排序数据直接存储到每个键的 JSON 字符串数据中。
  • 使用 SELECT id FROM table ORDER BY _ 访问 postgres,并使用 ids 直接从 hashmap 存储中提取

对于如何最好地解决此问题还有其他想法或建议吗?提前致谢。

【问题讨论】:

    标签: sorting caching redis


    【解决方案1】:

    因此,正如下面评论中提到的 Sorted Sets 是在缓存中实现排序和过滤功能的好方法。以以下示例为例,了解如何解决需要在散列中对对象进行排序的问题:

    1. 给定一个名为“movies”的散列,其方案为 bucket:objectId -> 对象,它是一个 JSON 字符串表示形式(阅读关于“分桶”散列以提高性能here.

    2. 创建一个名为“movieRatings”的排序集,其中每个成员都是来自“电影”哈希的 objectId,其分数是所有评分值的平均值(由数据库计算)。只需使用数字表示您要排序的任何内容,Redis 就可以为您提供很大的灵活性来提取所需的切片。

    3. 这个简单的方案在实现什么方面具有很大的灵活性 - 您只需向排序集询问一组符合您要求的键,然后使用 HMGET 从您的“电影”哈希中查找这些键。两次快速的 Redis 调用,问题解决了。

    4. 冲洗并重复您需要的任何类型的排序,例如“评论数”、“按字母顺序”、“演员数”等。过滤也可以通过这种方式完成,但可能是普通集足以达到这个目的。

    【讨论】:

    • 您的回答具有误导性,Redis 过滤和排序在一起不好,简单的例子来说明:给我类别 1 的电影,按名称排序,偏移 x,限制 y。如果 name 是一个排序集(仅用于高效排序大容量的结构),则 limit 和 offset 将包括不属于类别 1 的电影,因为那是不同的集或排序集。即使没有limit和offset,通过score查询一个sorted set的结果也不是redis set,所以不能高效的做进一步的相交。但是是的,如果您只需要订购结果或过滤(而不是两者),它就可以正常工作。
    • 皮埃尔,有些交集需要在应用程序代码中完成 - 是的。获取按名称排序的 id 列表(以您的语言使用的任何类似数组的结构),然后以编程方式执行对 Redis 的另一次调用,将这些 id 作为对表示“类别 1”的集合的查找。冲洗并重复。我的回答没有误导性,它只是关于如何在 Redis 中实现排序和过滤的综合教程。几乎不值得一票否决,国际海事组织。
    • 获取按名称排序的 id 列表将导致您从 redis 传输整个键集,然后将它们全部传输回与类别集相交,然后执行限制和偏移你的代码。你在这里真正从redis中获得了什么?仅在没有 Redis 的应用程序代码中对它进行基准测试。我已经在 Redis Lua 脚本中实现了这一点,但它仍然不适合高容量。您的回答听起来像过滤和排序组合在 Redis 中很好(支持),当它强制您从中检索整个集合时。 Redis 是其他场景的传奇......
    • 对于您的场景,如果您没有大量数据,它可能不会很慢。理想情况下,您需要的是 ORM 中的缓存功能,大多数成熟的功能都支持它,例如 hibernate (java) 或 activerecord (ruby/rails),不确定它是否在 django 中可用。或者,您可以将此表分片到另一个数据库中,这看起来就像您试图从 redis 获取的内容。
    • 这个答案很好地解释了它为什么不好:stackoverflow.com/questions/10205635/…
    【解决方案2】:

    这取决于您的需求。你的每一个策略都可以奏效。

    • 为每种方式存储辅助排序集的第一种方法 如果你有一个非常大的,你想订购是最好的方法 散列和/或您经常运行您的订单查询。这种方法将 如果您的哈希很大,则需要大量内存,但它也可以很好地扩展 就时间复杂度而言,随着您的哈希变大并且您开始 更频繁地运行订单查询。另一方面,它 在你的数据结构中引入了复杂性,感觉就像你是 尝试将 Redis 用于典型的数据库,如 Postgres、MySQL、 或者 Mongo 会更好。

    • 将订购数据直接存储到您的密钥中意味着您需要拉取 每次您进行订单查询时,您的整个哈希值。也许那不是 如果您的哈希非常小,或者您不经常进行有序查询,那就太糟糕了,但这根本不会扩展。

    • 如果您已经在使用 Postgres 获取密钥,为什么不将值也存储在 Postgres 中。这比点击 Postgres 然后点击 Redis 便宜得多,并且你的代码依赖的东西更少。 IMO,这可能是您最好的选择,并且最自然地工作。这样做,除非你有很好的理由不在 Postgres 中存储值,或者有一些非常大的速度问题,在这种情况下,请使用你的第一个策略。

    【讨论】:

    • 我同意这一点。看起来 OP 正在尝试在更适合关系数据库的地方使用 Redis。
    • 我不一定要尝试将 Redis 用作 RMDBMS。我只是想尽量减少我所做的数据库访问量。
    • @patrickn 我知道听到这个消息很糟糕,而且您比我们其他人更了解您的情况,但请认真考虑在这里继续使用 Redis 的成本和复杂性是否胜过废弃 Redis 并仅使用Postgres 本身。我不太了解您的情况,无法知道这是否适合您,但是如果 Redis 确实增加了复杂性和/或在您的情况下会损害性能。
    • 澄清一下,我不想在 Redis 中进行任何排序计算。我想用 Postgres 进行一次订单/平均计算并将它们存储在缓存中。这是缓存的一个常见用例,我想要做的唯一额外操作是使用 Postgres 中的这些缓存计算并生成一个键 -> 排名关系,我可以使用它在内存中进行快速排序操作。
    • 在查看了 Redis 排序集 API 之后,我认为使用我考虑的第一个方案可以相对轻松地实现这一点。如果它确实像我怀疑的那样有效,我会回来并发布我所做的作为正确答案。
    猜你喜欢
    • 2013-09-16
    • 2012-02-13
    • 2017-06-27
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 2013-09-27
    • 2013-01-28
    • 2010-09-16
    相关资源
    最近更新 更多