source

Python을 사용하여 Redis 데이터베이스의 모든 키 가져오기

manycodes 2023. 8. 4. 23:11
반응형

Python을 사용하여 Redis 데이터베이스의 모든 키 가져오기

사용 가능한 모든 키를 얻기 위해 Redis 명령에 대한 게시물이 있지만, 저는 그것을 Python으로 하고 싶습니다.

이거 할 수 있는 방법은?

사용하다scan_iter()

scan_iter()보다 우수함keys()모든 키를 메모리에 로드하는 대신 사용할 수 있는 반복기를 제공하기 때문에 많은 키에 사용할 수 있습니다.

레디스에 1B 레코드가 있어서 모든 키를 한 번에 돌려줄 만큼의 메모리를 얻을 수 없었습니다.

키를 하나씩 스캔

다음은 다음을 사용하는 파이썬 스니펫입니다.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()메소드, 작업이 원자적이지 않으며 부분적으로 실패할 수 있습니다.

명령줄에서 XARG를 사용하지 마십시오.

다른 곳에서 반복되는 이 예는 추천하지 않습니다.유니코드 키의 경우 실패하고 적당한 수의 키의 경우에도 매우 느립니다.

redis-cli --raw keys "user:*"| xargs redis-cli del

이 예에서 xargs는 모든 키에 대해 새 redis-cli 프로세스를 생성합니다. 이것은 좋지 않습니다.

저는 이 접근 방식을 모든 키를 하나씩 삭제한 첫 번째 파이썬 예제보다 4배 느리고 500개 묶음으로 삭제하는 것보다 20배 느립니다.

예, StrictRedis 모듈에서 사용합니다.

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

null 패턴을 지정하면 모든 패턴이 가져옵니다.링크된 페이지에 따라:

키(키='*')

패턴과 일치하는 키 목록을 반환합니다.

import redis
r = redis.Redis("localhost", 6379)
for key in r.scan_iter():
       print key

Pyredis 라이브러리 사용

스캔 명령

2.8.0 이후에 사용할 수 있습니다.

시간 복잡도: 모든 통화에 대해 O(1).커서가 0으로 돌아갈 만큼의 명령 호출을 포함하여 전체 반복에 대한 O(N).N은 집합 내부의 요소 수입니다.

패트릭의 답변과 다른 것들에 맞는 예시 코드를 추가하고 싶습니다.
키와 scan_iter 기법을 모두 사용하여 결과를 표시합니다.그리고 Python3는 izip_longest 대신 zip_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

rediscommand는 단일 스레드이므로 키()를 누르면 다른 rediscovery 작업을 차단할 수 있습니다.자세한 내용은 Redis에서 SCAN과 KEIES 성능 비교를 설명하는 훌륭한 게시물을 참조하십시오.

위의 승인된 답변에 추가합니다.

scan_iter와 함께 사용할 수 있습니다.count매개 변수를 사용하여 redis에 단일 반복 동안 여러 키를 검색하도록 지시합니다.이를 통해 특히 일치하는 패턴과 큰 키 공간에서 사용할 경우 키 가져오기 속도가 크게 향상됩니다.

카운트에 매우 높은 값을 사용할 경우 다른 동시 쿼리의 성능이 저하될 수 있으므로 엄격하게 사용해야 합니다.

https://docs.keydb.dev/blog/2020/08/10/blog-post/ 자세한 내용과 몇 가지 벤치마크가 있는 기사가 있습니다.

패트릭과 닐의 코드를 개선하고 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})

언급URL : https://stackoverflow.com/questions/22255589/get-all-keys-in-redis-database-with-python

반응형