在Redis数据库中迭代所有键和值的更快方法

7
我有一个包含大约 35 万个键的数据库。目前我的代码只是循环遍历所有键并从数据库中获取其值。
然而,这个过程需要将近 2 分钟,似乎非常慢,而 redis-benchmark 的结果为 100k reqs/3s。
我已经研究了流水线处理,但我需要返回每个值,以便最终得到一个键-值对的字典。
目前,我正在考虑在我的代码中使用线程来加速执行速度,这是处理这种用例的最佳方式吗?
以下是我目前拥有的代码。
import redis, timeit
start_time = timeit.default_timer()
count = redis.Redis(host='127.0.0.1', port=6379, db=9)
keys = count.keys()

data = {}

for key in keys:
    value = count.get(key)
    if value:
        data[key.decode('utf-8')] = int(value.decode('utf-8'))

elapsed = timeit.default_timer() - start_time

print('Time to read {} records: '.format(len(keys)), elapsed)

听起来你为了获取每个项目而查询了数据库。你能否改为进行更大的聚合查询,一次获取多个值? - Moberg
我知道的唯一方法是使用管道,但它返回值列表,我需要将键与值匹配。 - Jonathan
2个回答

5

首先,最快的方法是在EVAL中完成所有操作。

其次,推荐使用迭代所有键的SCAN方法。虽然它不会比KEYS更快地迭代,但它可以让Redis在处理其他操作时进行一些处理,因此它有助于整个应用程序的行为。

脚本将类似于local data={} local i=1 local mykeys=redis.call(\"KEYS\",\"*\") for k=1,#mykeys do local tmpkey=mykeys[k] data[i]={tmpkey,redis.call(\"GET\",tmpkey)} i=i+1 end return data,但如果您无法使用GET(例如集合和列表)获取键,则会失败。您需要添加错误处理。如果需要排序,可以直接在LUA中排序,或稍后在客户端上进行排序。后者会慢一些,但不会让Redis实例的其他用户等待。

示例输出:

127.0.0.1:6370> eval "local data={} local i=1 local mykeys=redis.call(\"KEYS\",\"*\") for k=1,#mykeys do local tmpkey=mykeys[k] data[i]={tmpkey,redis.call(\"GET\",tmpkey)} i=i+1 end return data" 0
1) 1) "a"
   2) "aval"
2) 1) "b"
   2) "bval"
3) 1) "c"
   2) "cval"
4) 1) "d"
   2) "dval"
5) 1) "e"
   2) "eval"
6) 1) "f"
   2) "fval"
7) 1) "g"
   2) "gval"
8) 1) "h"
   2) "hval"

你能给一个在Python中使用EVAL的例子吗?它似乎需要Lua脚本。我正在尝试使用管道技术,在迭代键时获取所有值,获取所有350k+键的值大约需要6秒钟,你知道这些值是否会按照键的顺序完全相同吗? - Jonathan
使用 KEYS 获取的值将与 keys = count.keys() 行中获得的顺序完全相同。Redis 的参考文档没有说明返回的顺序。 - Imaskar
另一方面,SCAN 明确不保证顺序和一致性。 - Imaskar
谢谢,我会测试这个。 - Jonathan
提醒:如果你有数百万个键,不要在生产环境中使用 redis.call(KEYS, *),否则它会锁定你的 Redis 直到完成。 - Clintm
显示剩余2条评论

4

我曾经遇到过同样的问题,最终使用KEYSMGET同时迭代多个键:

import redis
url='redis://my.redis.url'
query='product:*'

client = redis.StrictRedis.from_url(url, decode_responses=True)
keys = client.keys(query)

def chunks(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

partitions = list(chunks(keys, 10000))

data = []
for keys in partitions:
    values = client.mget(keys)
    data.extend(zip(keys, values))

print(len(data))

我写了一篇关于在将结果写入文件时显示进度的博客

这段代码是redis-mass-get Python包的基础。可以像这样使用它:

from redis_mass_get import RedisQuery

# pluralize will return the result or None
q = RedisQuery("redis://my.amazing.redis.url")

# query data 
data = q.query("product:*")
# data is returned as:
# [(key1, value1), (key2, value2)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接