从 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->articles
次 hgetall
次调用。请问最快的解决方案是什么?
我考虑过:
正在执行 HMGET
正在做MULTI/EXEC
使用 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
我有一个 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
次调用。请问最快的解决方案是什么?
我考虑过:
正在执行 HMGET
正在做MULTI/EXEC
使用 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