从 Redis 读取多个哈希的最快方法是什么?

What is the fastest way to read multiple hashes from Redis?

我有一个 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->articleshgetall 次调用。请问最快的解决方案是什么?

我考虑过:

  1. 正在执行 HMGET

  2. 正在做MULTI/EXEC

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

有什么想法吗?

如果您只是存储文章数据,我发现您应该将每篇文章 属性 存储在一个 per-article 散列中,但您应该构建一个散列,其中键应该是文章标识符,而该值应该是 JSON-serialized object 字符串。

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

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

您的 API 将所有 JSON object 作为字符串获取,并将它们 returns 直接发送到 API 客户端。

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

我检查了你的陈述:

Upon having this request:

api/v1/app/1/articles/20

在 REST 中,articles/20 会 "get the article 20 by id" 而不是 "get 20 articles"。

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

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

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

OP 在一些评论中说:

We only keep 20 articles at any given time. So when an app pushes a new article, the last one drops from the list and new one gets added to left of list. Then we remove the artice:{ID} hash. With your solution, I need to read the json serial string, remove the article:{ID} property, add the new one, then save it (and override the previous key). Some more work on the backend side. Is there no other way do get those hashes in a faster way apart from keeping them as a json serial? I know LUA could help Redis do it in one command, but I'm not sure if the load on Redis would remain same.

我的做法是:

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

    [1, 2]

  • 当您想存储一篇新文章时,您将文章序列化 object 并使用 hset 添加到 articles 哈希,然后添加使用 lpush 将文章 ID 添加到 articles:ids 列表。 使用 MULTI 命令来确保操作是自动完成的!.

  • 如果想按插入顺序获取文章,需要获取articles:ids个文章id,使用hmget获取所有文章

  • 当有20篇文章时,正如你评论中所说,你需要使用rpop命令在articles:id中获取最新的文章id,并且你使用 hdel 命令从 articles 哈希中删除文章 object。 使用 MULTI 命令来确保操作是自动完成的!.

更新 2:一些说明

OP 说:

How would I retrieve the articles, using HMGET? How well would that hash scale when it contains around a million of keys?

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

关于用百万键扩展散列的效果如何,请检查 hget 时间复杂度是否为 O(1),这意味着键的数量不会影响访问时间,而hmget(因为它是一个 哈希多次获取 )是 O(n) 因为访问时间随着获取的密钥数量而增加(而不是存储在哈希)。

顺便说一句,因为 Redis 3.x 是黄金,它在可扩展性方面提供了很大的改进,这要归功于 Redis Cluster, you should learn more about this new feature and how sharding 可以在大数据集的情况下提供帮助。

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