迭代redis db中所有键和值的更快方法
Faster way to iterate all keys and values in redis db
我有一个包含大约 350,000 个密钥的数据库。目前我的代码只是循环遍历所有键并从数据库中获取它的值。
然而这需要将近 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)
首先,最快的方法是在 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"
我有同样的问题,最终使用KEYS
和MGET
同时迭代多个键:
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))
I've written a blog on showing progress while writing the result to a file.
此代码是 redis-mass-get Python package 的基础。它可以用来做同样的事情,像这样:
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)]
我有一个包含大约 350,000 个密钥的数据库。目前我的代码只是循环遍历所有键并从数据库中获取它的值。
然而这需要将近 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)
首先,最快的方法是在 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"
我有同样的问题,最终使用KEYS
和MGET
同时迭代多个键:
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))
I've written a blog on showing progress while writing the result to a file.
此代码是 redis-mass-get Python package 的基础。它可以用来做同样的事情,像这样:
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)]