【问题标题】:What is the fastest way to read multiple hashes from Redis?从 Redis 读取多个哈希的最快方法是什么?
【发布时间】:2015-05-26 14:05:01
【问题描述】:

我有一个 LIST 包含指向一些 HASH 数据的指针。比如:

[LIST] app:1 ["article1", "article2", "article3" ...]
[HASH] article1 {title: "Hello", description: "World"}
[HASH] article2 {title: "Hello", description: "World"}
[HASH] article3 {title: "Hello", description: "World"}

收到此请求后:

api/v1/app/1/articles/20

我执行以下操作:

$pointers = $this->redis->lrange($appID, 0, $request->articles);
$articles = [];

foreach($pointers as $pointer) {
   $articles[] = $this->redis->hgetall($pointer);
}

所以我最终得到:1x lrange 呼叫,然后是 $request->articles 数量的 hgetall 呼叫。请问什么是最快的解决方案?

我想过:

  1. 做HMGET

  2. 执行 MULTI/EXEC

  3. 使用 LUA 编写此功能并在单个命令中获取它们。

有什么想法吗?

【问题讨论】:

    标签: redis predis


    【解决方案1】:

    如果您只是存储文章数据,我发现您应该将每个文章属性存储在每个文章的散列中,但您应该构建一个散列,其中键应该是文章标识符,而值应该是 JSON 序列化的对象字符串。

    通常,当您需要访问某个对象的特定属性时,您会使用散列,但我猜您获取这些文章是为了让它们在某些 UI 中列出,因此没有理由对每篇文章使用散列。无论如何,每篇文章的哈希值和所有文章的哈希值作为 JSON 可以共存:如果您需要访问特定文章属性而不获取整个对象,则为每篇文章的哈希值,以及所有文章的哈希值以获取整个对象对象或列出对象。

    想象一下,使用这种方法可以避免多少次 Redis 调用。您从列表中获取所有文章标识符,然后使用单个 hmget 命令在一次行程中获取所有文章。由于您使用的是lrange我知道您不会获得所有文章,但您使用的是分页

    您的 API 将所有 JSON 对象作为字符串获取,并将它们直接返回给 API 客户端。

    对您的 API 资源 URI 的一些担忧

    我检查了你的陈述:

    收到此请求后:

    api/v1/app/1/articles/20

    在 REST 中,articles/20 会“通过 id 获取文章 20”而不是“获取 20 篇文章”。

    让我建议你有两种方法可以解决范围问题:

    • 使用查询字符串:api/v1/app/1/articles?startFrom=0&max=20(参数名称只是我的建议...)。
    • 使用 HTTP 标头。您可以随请求一起发送 HTTP 标头,例如 MyApi-Range: 0 20,其中 0 是起始位置,20 是最大页面大小(即最大结果)。

    更新:有关该方法的一些细节。

    OP 在一些评论中说:

    我们在任何给定时间只保留 20 篇文章。所以当一个应用推送一个 新文章,最后一篇从列表中删除并添加新文章 到列表的左侧。然后我们删除 artice:{ID} 哈希。跟你的 解决办法,我需要读取json串口字符串,去掉 article:{ID} 属性,添加新的,然后保存它(并覆盖 上一个键)。在后端还有一些工作。有没有其他的 除了将它们保留为 json序列号?我知道 LUA 可以帮助 Redis 在一个命令中做到这一点,但我 不确定 Redis 上的负载是否会保持不变。

    我的做法是:

    • 文章存储在哈希 articles 中,其中键是文章 ID,值是 JSON 序列化文章对象:
    [1] => {title: "Hello", description: "World"}
    [2] => {title: "Hello 2", description: "World 2"}
    ....
    
    • 此外,您应该保持插入顺序,在名为的列表中添加文章 ID - 例如 - articles:ids:

      [1, 2]

    • 当你想存储一篇新文章时,你序列化文章对象并使用hset添加到articles哈希,你使用lpush将文章ID添加到articles:ids列表. 使用MULTI 命令确保操作以原子方式完成!

    • 如果要按广告订单获取文章,需要获取articles:ids文章id,使用hmget获取所有文章。

    • 当有20篇文章时,如您在评论中所说,您需要使用rpop命令获取articles:id中的最新文章id,然后使用hdel命令删除来自articles 哈希的文章对象。 使用MULTI 命令确保操作以原子方式完成!

    更新 2:一些说明

    OP 说:

    如何使用 HMGET 检索文章?那会有多好 包含大约一百万个键的哈希规模?

    关于如何使用hmget 检索文章,很简单:获取列表项(可能使用lrange),然后将所有获得的ID 作为hmget 的参数,以从哈希中获取整篇文章.

    关于使用数百万个键缩放散列的效果如何,请检查 hget 时间复杂度是否为 O(1),因此这意味着键的数量不会影响访问时间,而 hmget(因为它是hash multiple get) 是 O(n),因为访问时间会因获取的键数(而不是存储在哈希中的总键数)而增加。

    顺便说一句,由于 Redis 3.x 是黄金,并且由于Redis Cluster,它在可扩展性方面提供了很大的改进,您应该了解更多关于这个新功能以及sharding 在大型数据集的情况下如何提供帮助.

    【讨论】:

    • 所以你告诉我将所有文章作为序列化的 json 对象保存在一个字符串中?我觉得这不可能发生,可悲的是。因为我们保留了大约 20 篇文章,所以当我将一篇新文章推送到 LIST 时,我会删除最后一篇。如果我将它们保留为序列,我需要在更新时重写它,否则我最终会在序列中出现垃圾值。此外,最初我采用这种结构是因为它更有条理,并且是哈希的目的。我认为这比每次返回一个长的 json 序列要快。
    • 至于端点,无视。 :) 它实际上是一个监听这些请求的 POST 端点,但它必须这样做以避免我们在 URI 中带有 unicode 字符的 CORS/较旧的 jQuery 版本/站点遇到的一些问题。无论如何,它是一个内部 API,没有公共访问权限。
    • @Aristona 嘿,顺便说一句,我不明白将新文章推入列表并删除最后一篇的事情,我不明白在哈希中删除键或删除哈希
    • @Aristona 关于“long JSON”,你不觉得返回字符串比在哈希中逐个键访问并返回更快吗?
    • @Aristona 不客气,也欢迎来到 Redis 的精彩世界 :)
    【解决方案2】:

    将您的哈希键从 article1 更改为 app1:article1

    【讨论】:

    • 我做不到,我需要以最快的方式跟踪文章/应用程序的关系。它最终会使 Redis 扫描整个数据库,因为我无法执行 HGETALL app1 之类的操作,除非您在谈论不同的内容。
    • 好吧,我误解了你的问题。
    猜你喜欢
    • 2011-06-23
    • 2021-04-08
    • 1970-01-01
    • 2015-09-16
    • 2010-10-11
    • 1970-01-01
    • 2022-08-17
    • 2017-09-29
    • 2011-08-11
    相关资源
    最近更新 更多