有一篇关于Redis命令的帖子来获取所有可用的密钥,但我想用Python来做。
有什么办法吗?
答案 0 :(得分:74)
scan()
优于keys()
,因为它为您提供了一个可以使用的迭代器,而不是尝试将所有键加载到内存中。
我的redis中有一条1B记录,我无法获得足够的内存来立即返回所有密钥。
逐个扫描
这是一个python代码段,使用scan()
从商店获取与模式匹配的所有密钥并逐个删除它们:
import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
for key in r.scan_iter("user:*"):
# delete the key
r.delete(key)
扫描批次
如果您有一个非常大的要扫描的键列表 - 例如,大于> 100k键 - 批量扫描它们会更有效,如下所示:
import redis
from itertools import izip_longest
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# iterate a list in batches of size n
def batcher(iterable, n):
args = [iter(iterable)] * n
return izip_longest(*args)
# in batches of 500 delete keys matching user:*
for keybatch in batcher(r.scan_iter('user:*'),500):
r.delete(*keybatch)
我对此脚本进行了基准测试,发现使用500的批量大小比逐个扫描密钥快5倍。批量大小为500似乎是我Macbook Pro本地运行的最佳选择,它可能因网络而异。我测试了10,100,500,1000和10000的批量大小。如果您想看看我如何对其进行基准测试,请与我联系。
请注意,无论您使用scan()
还是keys()
方法,操作都不是原子操作,可能会在某个时间内失败。
在命令行中使用XARGS绝对避免
我不建议我在其他地方重复发现这个例子。它不适用于unicode键,即使是中等数量的键也非常慢:
redis-cli --raw keys "user:*"| xargs redis-cli del
在这个例子中,xargs为每个键创建一个新的redis-cli进程!亚克西。
我对这种方法进行了基准测试,比第一个python示例慢了4倍,在这个示例中,它逐个删除每个键,比以500个批量删除的速度慢20倍。
答案 1 :(得分:37)
是的,请使用StrictRedis模块中的keys()
:
>>> import redis
>>> r = redis.StrictRedis(host=YOUR_HOST, port=YOUR_PORT, db=YOUR_DB)
>>> r.keys()
给出一个空模式将获取所有这些模式。根据页面链接:
键(图案= '*')
返回匹配模式
的键列表
答案 2 :(得分:8)
import redis
r = redis.Redis("localhost", 6379)
for key in r.scan_iter():
print key
使用Pyredis库
自2.8.0以来可用。
时间复杂度:每次通话都是O(1)。 O(N)进行完整的迭代,包括足够的命令调用,使光标返回0. N是集合中元素的数量..
答案 3 :(得分:1)
对上述已接受答案的补充。
scan_iter
可以与 count
参数一起使用,以便告诉 redis 在单次迭代期间搜索多个键。这可以显着加快键的获取速度,尤其是在与匹配模式和大键空间一起使用时。
对计数使用非常高的值时要小心谨慎,因为这可能会破坏其他并发查询的性能。
https://docs.keydb.dev/blog/2020/08/10/blog-post/ 这是一篇包含更多详细信息和一些基准测试的文章。
答案 4 :(得分:0)
我想添加一些示例代码以与Patrick的答案以及其他代码一起使用。
这显示了使用键和scan_iter技术的结果。
并且请注意,Python3使用zip_longest而不是izip_longest。下面的代码循环遍历所有键并显示它们。我将batchsize设置为变量12,以减小输出。
我写这篇文章是为了更好地理解密钥的批处理如何工作。
import redis
from itertools import zip_longest
\# connection/building of my redisObj omitted here
\# iterate a list in batches of size n
def batcher(iterable, n):
args = [iter(iterable)] * n
return zip_longest(*args)
result1 = redisObj.get("TestEN")
print(result1)
result2 = redisObj.get("TestES")
print(result2)
print("\n\nLoop through all keys:")
keys = redisObj.keys('*')
counter = 0
print("len(keys)=", len(keys))
for key in keys:
counter +=1
print (counter, "key=" +key, " value=" + redisObj.get(key))
print("\n\nLoop through all keys in batches (using itertools)")
\# in batches of 500 delete keys matching user:*
counter = 0
batch_counter = 0
print("Try scan_iter:")
for keybatch in batcher(redisObj.scan_iter('*'), 12):
batch_counter +=1
print(batch_counter, "keybatch=", keybatch)
for key in keybatch:
if key != None:
counter += 1
print(" ", counter, "key=" + key, " value=" + redisObj.get(key))
示例输出:
Loop through all keys:
len(keys)= 2
1 key=TestES value=Ola Mundo
2 key=TestEN value=Hello World
Loop through all keys in batches (using itertools)
Try scan_iter:
1 keybatch= ('TestES', 'TestEN', None, None, None, None, None, None, None, None, None, None)
1 key=TestES value=Ola Mundo
2 key=TestEN value=Hello World
请注意,redis命令是单线程的,因此执行keys()可能会阻止其他redis活动。请参见此处的精彩文章,其中更详细地说明了这一点:SCAN vs KEYS performance in Redis