[英]Get all keys in Redis database with python
有一篇关于获取所有可用密钥的 Redis 命令的帖子,但我想用 Python 来做。
有什么办法可以做到这一点?
使用scan_iter()
对于大量键, scan_iter()
优于keys()
,因为它为您提供了一个可以使用的迭代器,而不是尝试将所有键加载到内存中。
我的 redis 中有 1B 条记录,我永远无法获得足够的内存来一次返回所有键。
一个接一个地扫描密钥
这是一个 python 片段,使用scan_iter()
从存储中获取与模式匹配的所有键并一个接一个地删除它们:
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 倍。 我测试了不同的批量大小(3,50,500,1000,5000),发现 500 的批量大小似乎是最佳的。
请注意,无论您使用scan_iter()
还是keys()
方法,操作都不是原子的,可能会在中途失败。
绝对避免在命令行上使用 XARGS
我不推荐这个我在其他地方发现重复的例子。 对于 unicode 键,它会失败,即使是中等数量的键也会非常慢:
redis-cli --raw keys "user:*"| xargs redis-cli del
在此示例中,xargs 为每个键创建一个新的 redis-cli 进程! 那很糟。
我对这种方法进行了基准测试,它比第一个 python 示例慢 4 倍,它一个一个地删除每个键,比批量删除 500 个键慢 20 倍。
是的,使用 StrictRedis 模块中的keys()
:
>>> import redis
>>> r = redis.StrictRedis(host=YOUR_HOST, port=YOUR_PORT, db=YOUR_DB)
>>> r.keys()
给出一个空模式将获取所有这些。 根据链接的页面:
键(模式='*')
返回匹配模式的键列表
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 是集合内的元素数。
我想添加一些示例代码来配合帕特里克的回答和其他人。
这显示了使用键和 scan_iter 技术的结果。 请注意 Python3 使用 zip_longest 而不是 izip_longest。 下面的代码循环遍历所有键并显示它们。 我将批量大小作为变量设置为 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
对上面接受的答案的补充。
scan_iter
可以与count
参数一起使用,以告诉 redis 在单次迭代期间搜索多个键。 这可以显着加快键的获取速度,尤其是在与匹配模式和大键空间一起使用时。
使用非常高的计数值时要小心,因为这可能会破坏其他并发查询的性能。
https://docs.keydb.dev/blog/2020/08/10/blog-post/这是一篇包含更多细节和一些基准的文章。
我改进了 Patrick 和 Neal 的代码,并添加了对 csv 的导出:
import csv
import redis
from itertools import zip_longest
redisObj = redis.StrictRedis(host='localhost', port=6379, db=0, decode_responses=True)
searchStr = ""
# iterate a list in batches of size n
def batcher(iterable, n):
args = [iter(iterable)] * n
return zip_longest(*args)
with open('redis.csv', 'w', newline='') as csvfile:
fieldnames = ['key', 'value']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
print("\n\nLoop through all keys in batches (using itertools)")
counter = 0
batch_counter = 0
print("Try scan_iter:")
for keybatch in batcher(redisObj.scan_iter('*'), 500):
batch_counter +=1
#print(batch_counter, "keybatch=", keybatch)
for key in keybatch:
if key != None:
counter += 1
val = ""
if (searchStr in key):
valType = redisObj.type(key)
print(valType)
match valType:
case "string":
val = redisObj.get(key)
case "list":
valList = redisObj.lrange(key, 0, -1)
val = '\n'.join(valList)
case "set":
valList = redisObj.smembers(key)
val = '\n'.join(valList)
case "zset":
valDict = redisObj.zrange(key, 0, -1, False, True)
val = '\n'.join(['='.join(i) for i in valDict.items()])
case "hash":
valDict = redisObj.hgetall(key)
val = '\n'.join(['='.join(i) for i in valDict.items()])
case "stream":
val = ""
case _:
val = ""
print(" ", counter, "key=" + key, " value=" + val)
writer.writerow({'key': key, 'value': val})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.