使用python获取Redis数据库中的所有key

问题描述 投票:0回答:6

有一篇关于 Redis 命令获取所有可用密钥的帖子,但我想用 Python 来完成。

有什么办法可以做到这一点吗?

python database redis
6个回答
191
投票

使用

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 倍,在第一个 Python 示例中,它逐一删除每个键,比批量删除 500 个键慢 20 倍。


83
投票

是的,使用 StrictRedis 模块中的

keys()

>>> import redis
>>> r = redis.StrictRedis(host=YOUR_HOST, port=YOUR_PORT, db=YOUR_DB)
>>> r.keys()

给出一个空模式将获取所有它们。根据链接页面:

按键(模式='*')

返回匹配模式的键列表


22
投票
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 是集合内元素的数量..


4
投票

对上面已接受的答案的补充。

scan_iter
可以与
count
参数一起使用,以便告诉 Redis 在单次迭代期间搜索多个键。这可以显着加快密钥获取速度,尤其是与匹配模式和大密钥空间一起使用时。

使用非常高的计数值时要小心,因为这可能会破坏其他并发查询的性能。

https://docs.keydb.dev/blog/2020/08/10/blog-post/这是一篇包含更多详细信息和一些基准的文章。


3
投票

我想添加一些示例代码来配合帕特里克的答案和其他人的答案。
这显示了使用键和 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


3
投票

我改进了 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})
© www.soinside.com 2019 - 2024. All rights reserved.