SCAN 이란?
Redis는 인-메모리 데이터 구조 스토어이다.
Redis는 Key-Value 데이터 구조를 사용하여, 데이터를 메모리에 유지하고,
매우 빠른 성능으로 데이터를 읽고 쓰기가 가능하다.
Redis는 데이터베이스에 저장된 키/값 쌍의 목록을 관리하는 데
사용할 수 있는 다양한 명령어를 제공한다.
"SCAN"은 이러한 명령어 중 하나로,
Redis 데이터베이스에서 키/값 쌍을 반복적으로 조회하는 데 사용된다.
Redis KEYS 명령어를 사용하여 Redis 데이터베이스에서
특정 패턴에 일치하는 키를 찾아 반환할 수 있지만,
싱글스레드를 사용하는 Redis 구조의 특성상 다른 모든 일을 제쳐두고
해당 명령만 수행하게 되므로 (작업시간 O(N) 소요)
대규모 데이터베이스에서 사용할 때는 Redis 서버의 부하를 매우 크게 일으킬 수 있으므로
실제 운영환경에서는 "절대" 사용해서는 안된다.
반면에 SCAN 은 cursor를 통해서 O(1)의 작업시간을 필요로 해서
KEYS 명령어보다 훨씬 안정적으로 키를 찾을 수 있다.
아래의 그림을 참고하면 KEYS 명령어를 왜 사용하면 안되는지
이해하는데에 도움이 될 것이다.
SCAN 사용 방법
Redis에서 Scan 사용테스트를 진행하려면
일단 Redis 에 데이터를 넣어야 한다.
데이터를 넣기 위해 파이썬을 이용하였다.
아래와 같은 파이썬 코드로 테스트 데이터를 넣어주자.
redis-py-cluster 라이브러리를 사용해야 하므로
만약 해당 라이브러리가 없다면 아래와 pip3 를 통해서 설치해 주길 바란다.
pip3 install redis-py-cluster
현재 Redis 구조는 테스트 환경이므로 Slave 환경은 존재하지 않는
Cluster 구성으로 이루어져 있다.
해당 클러스터 구조 내에서 데이터를 넣어주는 테스트 코드는 아래와 같다.
from rediscluster import RedisCluster
"""
Functions that connect to the Redis Cluster
"""
def redis_connection(startup_nodes,redis_password):
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True, password=redis_password)
try:
rc.ping()
return rc
except Exception as e:
return None
"""
Function that puts customized data in Redis
"""
def redis_input_data(rc,key_name,key_value,cnt):
for i in range(cnt):
rc.set(f"{key_name}{i}", f"{key_value}{i}")
"""
main function
"""
def main():
startup_nodes = [{"host": "127.0.0.1", "port": "6380"}, ...]
rc = redis_connection(startup_nodes,'1234')
redis_input_data(rc,'tstory_test','tstory_value',10000)
rc.close()
"""
main function - execute
"""
if __name__ == '__main__':
main()
redis_input_data 함수를 통해 특정 패턴의 key-value 데이터를
1만 건을 넣어주려고 한다.
그럼 아래와 같이 Redis에 데이터가 들어가게 된다.
Redis 도 분산형 데이터베이스이기 때문에,
위의 그림과 같이 Key-Value Data 가 분산되어 저장될 것이다.
그럼 Redis-cli 환경에 접속하여, 데이터가 잘 들어갔는지 확인해 보자.
# Redis-cli 를 통한 Redis 접속
redis-cli -h {ip} -p {port}
# 암호를 걸어놨다면 암호기입 진행
127.0.0.1:6379 > auth 1234
# SCAN 진행
127.0.0.1:6379 > SCAN 0 MATCH tstory_test* COUNT 1000
SCAN 명령어를 각 서버에서 실행해주면 위의 그림과 같이
각 데이터가 고르게 분산되어 저장된 것을 볼 수 있다.
특정 Key Pattern을 만족하는 key 개수 찾기
운영업무를 수행할 때, 특정 key pattern을 만족하는
key data 건수가 얼마나 되는지 확인해 보는 경우가 발생할 수 있다.
이럴 경우에도 KEYS 명령어가 아닌 SCAN을 통해서
데이터 건수를 확인할 수 있다.
SCAN 명령어는 잘 살펴보면 아래와 같이 key와
다음 커서값(아래의 그림에서 "764")을 반환하는 것을 볼 수 있다.
즉, SCAN을 통해서 해당 key의 개수를 파악해 주고
다음 cursor 가 반환되니 해당 cursor를 시작으로 다시 SCAN을 진행해 주면 된다.
만약에 다음 데이터가 없다면 cursor는 0으로 반환되게 된다.
또한, 각 노드마다 key가 분산되어 퍼져있으므로,
각 노드별로 cursor관리를 해줘야 한다.
그럼 해당 기능을 파이썬으로 구현해 보면 아래와 같이 구현할 수 있다.
from rediscluster import RedisCluster
"""
Functions that connect to the Redis Cluster
"""
def redis_connection(startup_nodes,redis_password):
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True, password=redis_password)
try:
rc.ping()
return rc
except Exception as e:
return None
"""
Function that finds specific key patterns in the Redis Cluster through SCAN
"""
def redis_key_scan(rc,redis_key_prefix,scan_cnt):
master_nodes_list = []
nodes = rc.connection_pool.nodes.nodes.values()
for node in nodes:
if (node['server_type'] == 'master'):
master_nodes_list.append("{}:{}".format(node['host'],node['port']))
total_key_cnt = 0
for node in master_nodes_list:
cursor = 0
while True:
cursor, keys = rc.scan(cursor=cursor, match=redis_key_prefix, count=scan_cnt)[node]
total_key_cnt += len(keys)
if cursor == 0:
break
return total_key_cnt
"""
main function
"""
def main():
startup_nodes = [{"host": "127.0.0.1", "port": "6380"}, ...]
rc = redis_connection(startup_nodes,'1234')
print(redis_key_scan(rc,"tstory_test*",1000))
"""
main function - execute
"""
if __name__ == '__main__':
main()
'Redis' 카테고리의 다른 글
[Redis] key 삭제 api 구현 (0) | 2023.06.12 |
---|---|
[Redis] Key 삭제 (0) | 2023.05.24 |
[Redis] Redis 설치 (0) | 2022.10.12 |
[Redis] Redis Cluster (0) | 2022.08.11 |
[Redis] Redis 란? (0) | 2022.08.10 |