【问题标题】:Redis scan count: How to force SCAN to return all keys matching a pattern?Redis 扫描计数:如何强制 SCAN 返回与模式匹配的所有键?
【发布时间】:2015-10-16 09:23:32
【问题描述】:

我正在尝试找出存储在与 redis 中的模式匹配的键列表中的值。我尝试使用SCAN,以便稍后我可以使用MGET 获取所有值,问题是:

SCAN 0 MATCH "foo:bar:*" COUNT 1000

不返回任何值而

SCAN 0 MATCH "foo:bar:*" COUNT 10000

返回所需的键。 我如何强制SCAN 查看所有现有密钥?我必须为此研究 lua 吗?

【问题讨论】:

  • 强制 SCAN 一次性完成整个键空间相当于运行 KEYS。请注意,正是为此目的引入了 SCAN - 不运行 KEYS。
  • @ItamarHaber 是否也像 KEYS 命令对完整迭代一样阻止键空间(一次迭代)?
  • 是的 - 几乎所有的操作都是阻塞的。
  • count参数的最大值有什么建议吗?
  • "由于这些命令允许增量迭代,每次调用只返回少量元素,它们可以在生产中使用,而不会像 KEYS 或 SMEMBERS 这样可能会长时间阻塞服务器的命令的缺点对大量键或元素调用时的时间(甚至几秒钟)。”

标签: redis


【解决方案1】:

使用下面的代码,您将从光标 0 开始扫描 1000 个第一个对象

SCAN 0 MATCH "foo:bar:*" COUNT 1000 

结果,你会得到一个新的光标来调用

SCAN YOUR_NEW_CURSOR MATCH "foo:bar:*" COUNT 1000

扫描 1000 个下一个对象。然后,当您将 COUNT 从 1000 增加到 10000 并检索数据时,您会扫描更多键,然后在您的情况下匹配更多键。

要扫描整个列表,您需要调用 SCAN 直到光标给出响应返回零(即整个扫描)

使用INFO 命令获取您喜欢的密钥数量

db0:keys=YOUR_AMOUNT_OF_KEYS,expires=0,avg_ttl=0

然后调用

SCAN 0 MATCH "foo:bar:*" COUNT YOUR_AMOUNT_OF_KEYS

【讨论】:

  • 我如何强制 SCAN 一次性查看所有现有密钥以查看是否存在匹配项?
  • 如果redis中有很多key,这会太慢吧?我想我将不得不重新考虑我的方法?一次性搜索键或循环遍历光标哪个更好?
  • 是的,这不是生产中的正确方法。更好的是迭代光标,SCAN 就是为此而设计的。迭代直到光标返回零以进行完整扫描。这将每次扫描计数值。
  • @DarthSpeedious 如果对您有好处,请将其标记为答案。谢谢
  • 请注意,在大型生产数据库中,SCAN 可能仍然存在性能问题,因为您的应用需要多次(甚至大量)调用 redis。例如查看此讨论:github.com/xetorthio/jedis/issues/1338
【解决方案2】:

只是把这个放在这里给任何对如何使用 python redis 库感兴趣的人:

import redis
redis_server = redis.StrictRedis(host=settings.redis_ip, port=6379, db=0)
mid_results = []
cur, results = redis_server.scan(0,'foo:bar:*',1000)
mid_results += results

while cur != 0:
    cur, results = redis_server.scan(cur,'foo:bar:*',1000)
    mid_results += results

final_uniq_results = set(mid_results)

我花了几天时间才弄明白,但基本上每个scan 都会返回一个元组。

例子:

(cursor, results_list)

(5433L, [... keys here ...])
(3244L, [... keys here, maybe ...])
(6543L, [... keys here, duplicates maybe too ...])
(0L, [... last items here ...])
  • 继续扫描cursor,直到它返回0。
  • 保证会返回0。
  • 即使扫描在两次扫描之间返回一个空的results_list。
  • 但是,正如 @Josh 在 cmets 中所指出的,SCAN 不能保证在插入同时发生的竞争条件下终止。

我很难弄清楚光标编号是什么以及为什么我会随机得到一个空列表或重复的项目,但即使我知道我刚刚放入了项目。

看完后:

这更有意义,但仍有一些深刻的编程魔法和妥协发生在迭代集合。

【讨论】:

  • +1 非常感谢。不过我要补充一点,SCAN 不保证会终止。在大多数情况下它会,但如果数据库中的项目数继续增长并超过迭代器,它就不会。来自 Redis 官方文档:...is guaranteed to terminate only if the size of the iterated collection remains bounded to a given maximum size.
  • @Josh 真棒点,谢天谢地我没有遇到这个问题,但它很有意义。
【解决方案3】:

如果您在redis python 库上使用scan_iter 方法,请注意这是一项微不足道的任务:

from redis import StrictRedis

redis = StrictRedis.from_url(REDIS_URI)

keys = []
for key in redis.scan_iter('foo:bar:*', 1000):
    keys.append(key)

最后,keys 将包含您通过应用 @khanou 的方法获得的所有密钥。

这也比使用 shell 脚本更有效,因为它们会在循环的每次迭代中生成一个新客户端。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-09
    • 2015-11-05
    • 1970-01-01
    相关资源
    最近更新 更多