用Python获取Redis数据库中的所有键
有一篇帖子讲了一个Redis命令,可以用来获取所有可用的键,但我想用Python来实现这个功能。
有没有什么方法可以做到这一点呢?
6 个回答
在上面接受的答案基础上,补充一点。
scan_iter
可以和一个叫count
的参数一起使用,这样可以让redis在一次操作中搜索多个键。这可以大大加快获取键的速度,特别是当你使用匹配模式并且在大量键的情况下。
不过,要小心使用过高的count
值,因为这可能会影响其他同时进行的查询的性能。
https://docs.keydb.dev/blog/2020/08/10/blog-post/ 这里有一篇文章,里面有更多细节和一些性能测试的数据。
我想给Patrick的回答和其他人的回答加一些示例代码。
这个示例展示了使用键和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与KEYS在Redis中的性能比较
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 是集合中元素的数量。
没错,可以使用keys()
这个方法,它来自StrictRedis模块:
>>> import redis
>>> r = redis.StrictRedis(host=YOUR_HOST, port=YOUR_PORT, db=YOUR_DB)
>>> r.keys()
如果你给它一个空的模式,它会把所有的键都取出来。根据链接中的说明:
keys(pattern='*')
这个方法会返回一个符合模式的键的列表
使用 scan_iter()
scan_iter()
比 keys()
更适合处理大量键,因为它提供了一个迭代器,你可以逐个处理,而不是试图一次性把所有键都加载到内存中。
我在我的 Redis 数据库里有10亿条记录,根本无法找到足够的内存一次性返回所有的键。
逐个扫描键
下面是一个使用 scan_iter()
的 Python 代码片段,它可以从存储中获取所有匹配某个模式的键,并逐个删除:
import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
for key in r.scan_iter("user:*"):
# delete the key
r.delete(key)
批量扫描
如果你要扫描的键的数量非常大,比如超过10万个,分批扫描会更有效率,像这样:
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倍。