繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM