【问题标题】:Get Redis values while scanning扫描时获取 Redis 值
【发布时间】:2026-02-04 14:15:01
【问题描述】:

我以这种方式创建了 Redis 键/值索引:

set 7:12:321 '{"some:"JSON"}'

key由冒号分隔,key的每一部分代表一个层次索引。 get 7:12:321 表示我知道确切的层次结构并且只想要一个项目

scan 7:12:* 表示我想要第一层层次结构中 id 7 和第二层层次结构中 id 12 下的每个项目。

问题是:如果我想要 JSON 值,我必须首先 scan(在几毫秒内约 50000 个条目)然后 get 逐个扫描返回的每个键(800 毫秒)。

这不是很有效。这是我在 * 搜索“扫描 Redis 值”时找到的唯一答案。

1/ 是否有另一种扫描 Redis 以获取值或键/值对而不仅仅是键的方法?我尝试了hscan,如下:

hset myindex 7:12:321 '{"some:"JSON"}' hscan myindex MATCH 7:12:* 但它破坏了性能(50000 个条目几乎是 4s)

2/ Redis 中是否有另一种数据结构我可以以相同的方式使用,但可以“扫描值”(hset?)

3/ 我是否应该使用另一种数据存储解决方案(例如 PostgreSQL ltree?)以适应我的用例并具有巨大的性能?

我一定遗漏了一些非常明显的东西,因为这听起来像是一个常见的用例。

感谢您的回答。

【问题讨论】:

    标签: postgresql redis key-value-store


    【解决方案1】:

    针对您当前的解决方案进行优化

    您应该使用mget 批量获取键值对,或者使用pipeline 来减少RTT,而不是get 一个接一个地处理scan 返回的每个键。

    您当前解决方案的效率问题

    scan 命令迭代数据库中的所有键,即使匹配模式的键的数量很少。键数增加时性能下降。

    另一种解决方案

    由于层次索引是一个整数,您可以将层次索引编码为一个数字,并将该数字用作排序集的分数。通过这种方式,您可以按分数范围搜索,而不是按模式搜索,这对于排序集来说非常快。以以下为例。

    比如说,第一个(最右边的)分层索引小于1000,第二个索引小于100,那么你可以将索引(例如7:12:321)编码成一个分数(321 + 12 * 1000 + 7 * 100 * 1000 = 712321)。然后将分数和值设置为有序集合:zadd myindex 712321 '{"some:"JSON"}'

    当您要搜索匹配7:12:* 的键时,只需使用zrangebyscore 命令获取分数在712000 和712999 之间的数据:zrangebyscore myindex 712000 712999 withscores

    通过这种方式,您可以将key(与返回的分数解码)和value一起获取。它也应该比scan 解决方案更快。

    更新

    解决方案有个小问题:有序集合的成员必须是唯一的,所以你不能有2个具有相同值的键(即json字符串)。

    // insert OK
    zadd myindex 712321 '{"the_same":"JSON"}'
    // failed to insert, members should be unique
    zadd myindex 712322 '{"the_same":"JSON"}'
    

    为了解决这个问题,可以将key与json字符串结合起来,使其唯一:

    zadd myindex 712321 '7:12:321-{"the_same":"JSON"}'
    zadd myindex 712321 '7:12:322-{"the_same":"JSON"}'
    

    【讨论】:

    • 非常感谢您的回答。分数的解决非常巧妙。我当然必须对其进行调整,因为层次键并没有像我在问题中提到的那样“纯粹”地组织起来(我过于简化了)。但是围绕这个想法肯定有一些值得尝试的东西。与此同时,我在文档中发现了关于 mget 的内容,我可以看到改进,但是,是的,肯定不如你的解决方案聪明 ;) 谢谢!
    • @Javier92 我发现原来的解决方案有点问题。请参阅我的更新以获取解决方案。
    • 别担心,密钥的第一部分 + 最后一部分是唯一标识符。中间的部分仅用于索引目的。所以很容易创建一些独特的 json 字段。
    【解决方案2】:

    只要您只需要执行前缀搜索,您就可以考虑使用排序集和字典范围。有关此内容和一般索引的更多信息,请参阅http://redis.io/topics/indexes

    更新了一个例子:

    考虑以下 -

    $ redis-cli
    127.0.0.1:6379> ZADD anotherindex 0 '7:12:321:{"some:"JSON"}'
    (integer) 1
    127.0.0.1:6379> ZRANGEBYLEX anotherindex [7:12: [7:12:\xff
    1) "7:12:321:{\"some:\"JSON\"}"
    

    现在去阅读这方面的内容,以便您 1) 了解它的作用以及 2) 知道如何避免可能的陷阱 :)

    【讨论】:

    • 感谢您的回答,但不确定我是否理解正确。在您提到的文档中,ZADD 始终以0 作为键。因此,我作为关键的层次结构变成了价值?我的 JSON 将存储在哪里。对不起,如果这对你来说是微不足道的。您能否举例说明您对 ZADDZRANGEBYLEX 的想法?非常感谢。
    • 感谢更新示例,尽管我现在才意识到!非常感激。我肯定会用它来搜索前缀