레디스를 잘 운영하기 위한 대시보드 모니터링, 레디스 마이그, 장애 유발 가능한 환결 설정/커맨드를 알아보자
레디스 모니터링 구축하기

- 익스포터: 상태를 실시간으로 스크랩해 메트릭 수집하는 프로그램
- 프로메테우스: 메트릭 기반 오픈소스 모니터링 시스템
- 지정한 타깃(익스포터)으로 HTTP로 직접 접근해 시계열 데이터를 pull방식으로 수집한다.
- promQL 쿼리 지원
- 그라파나: 오픈소스 메트릭 데이터 시각화 도구
- 프로메테우스 및 여러 데이터소스를 추가해 메트릭을 시각화해서 볼 수 있다.
- 얼럿 매니저: 프로메테우스에서 설정한 알람 규칙 임계치 도달하면 얼럿 매니저를 통해 sms/이메일/슬랙 등으로 전송할 수 있다.
설치
익스포터 설치
- 노드 익스포터
- 레디스 익스포터
얼럿 매니저 설치
프로메테우스 설치
그라파나 설치
레디스 플러그인으로 대시보드 만들기
- 프로메테우스 필요 없음
- 그라파나에서 플러그인 설치하면 됨
레디스 버전 업그레이드
- 레디스는 릴리즈 주기가 빠른 편이라 EOL(End Of Life)도 짧은 편이다. 그러니 주기적으로 버전업하는게 좋다.
- 운영중 레디스 업그레이드 하는 2가지 방법 (센티널, 클러스터 없이)
- 새 버전 레디스를 새 서버에 설치하고, 기존 버전 레디스의 데이터를 복제한다. (접속 정보 변경 필요)
- 실행 중 레디스 중지하고 신규 레디스로 재실행하기. (다운타임 발생)
→ 센티널, 클러스터 구조면 복제본부터 업그레이드하면 돼서 다운타임 없음, 접속 정보 변경 필요 없음
센티널일 때 레디스 버전업하기
- 신규 버전 레디스 바이너리 파일 다운
-------------------------------------------------------
- 센티널 3대 종료
- 신규 버전 폴더에
sentienl.conf복사 - 센티널 시작
-------------------------------------------------------
- 복제본 중단
- 신규 버전 폴더에
redis.conf복사 - 복제본 시작
- 센티널에서 수동 페일오버 수행 (마스터↔복제본 스위치)
-------------------------------------------------------
- 구 마스터 중단
- 신규 버전 폴더에
redis.conf복사 - 마스터 시작
- 센티널에서 수동 페일오버(페일백) 수행
클러스터일 때 레디스 버전업하기
- 센티널 인스턴스 신경 안 써도 돼서 더 간단
- 복제본 먼저 버전업 하고 → 페일오버 해서 → 구 마스터 마저 버전업하기
레디스 운영 가이드
운영 중 장애 종료, 성능 저하 요소, 모니터링할 지표, 장애 시나리오 알려주겠다.
장애 또는 성능 저하 유발할 수 있는 레디스 설정
maxmemory-policy
- 메모리 한계 도달 시 어떤 키 제거할지 정하는 정책
- 기본은
noeviction으로 데이터 삭제하지 않고 에러 반환함. 그럼 장애로 이어질 수 있어서 데이터 삭제될 수 있게allkeys-lru권장
stop-writes-on-bgsave-error
- RDB 스냅샷 정삭적으로 저장되지 않으면 레디스로의 모든 쓰기 작업 중지함
- 더 큰 장애 유발될 수 있어서 설정 비활성화 권장 (
no로)
자동 백업 옵션
- RDB, AOF로 백업 파일 생성하는건 레디스에 큰 부하임
- 포그라운드면 다른 작업들 대기해야해서 성능 영향 미침
- 백그라운드면 COW(Copy-On-Write)라 메모리 사용량이 최대
maxmemory의 두 배까지 증가 가능 (OOM 발생 가능)
- 그러니 백업은 의도한 시간에 수행되도록 수동으로 시간 설정하는게 좋다.
1) save 옵션 해제
127.0.0.1:6379> CONFIG GET save
1) "save"
2) "3600 1 300 100 60 10000" # 3600초(1시간) 동안 최소 1번 or 300초(5분) 동안 최소 100번 or 60초(1분) 동안 최소 10000번 변경 작업이 수행되면 자동으로 RDB 파일 생성
# 해제
127.0.0.1:6379> CONFIG SET save ""
OK
원하는 시간에 백업 수행하게 하려고
2) auto-aof-rewrite-percentage 옵션 해제
appendonly yes 로 AOF 형식 백업인 경우, 기본 AOF 파일이 기존 AOF 파일보다 100%증가하면 재작성됨
127.0.0.1:6379> CONFIG GET auto-aof*
1) "auto-aof-rewrite-min-size"
2) "67108864"
3) "auto-aof-rewrite-percentage"
4) "100"
127.0.0.1:6379> CONFIG SET auto-aof-rewrite-percentage 0
OK
0으로 바꿔서 자동 재작성 방지
min size, percentage 두 조건이 AND로 충족해야 자동 재작성되기 때문에 하나만 0으로 만들면 됨
레디스 운영 및 성능 최적화
오래 걸리는 커맨드 사용
- O(N)이상 시간복잡도 걸리는 커맨드는 지양해야한다.
KEYS,FLUSHALL에 더불어set,list,hash처럼 한 자료 구조 안에 여러 아이템 갖고 있는 경우도 아이템 개수에 비례해 실행 시간이 증가한다.
키스페이스 커맨드
※키스페이스: 키공간 확인 (ex. 현재 DB에 키가 몇 개나 있고, 메모리를 얼마나 쓰고 있는지, 키 중심으로 발생하는 이벤트)
- 시간복잡도에서의 N: 전체 키의 개수
KEYS
- 모든 키 접근하기 때문에 O(N)
SCAN으로 대체하는게 좋다.
FLUSHALL
- ASYNC 옵션 없으면 동기로 동작해 O(N)
- 모든 키 삭제하는데,
FLUSHALL실행 중 새로 생성된 키는 삭제하지 않는다.
FLUSHDB
- ASYNC 옵션 없으면 동기로 동작해 O(N)
- DB에 속한 모든 키 삭제함
-
구분 Standalone (FLUSHDB) Cluster (FLUSHDB) 삭제 단위 선택된 논리 DB (예: DB 0) 현재 접속한 노드 전체 다른 데이터 영향 다른 번호(DB 1~15) 데이터는 안전함 다른 노드(Master B, C) 데이터는 안전함 전체 삭제하려면? FLUSHALL실행모든 마스터 노드에서 각각 실행해야 함
NOTE: LAZY FREEING
- 키 삭제인 DEL은 동기로 삭제, 비동기로 하려면 UNLINK로 삭제
- 레디스가 시스템적으로 삭제하는 경우, 기본은 동기 삭제인데 설정값 변경해서 비동기로 삭제하게 할 수 있다.
lazyfree-lazy-evction: maxmemory로 인한 키 이빅션lazyfree-lazy-expire: 만료 시간된 키 삭제lazyfree-lazy-server-del: 서버 동기화나 다른 명령 사이드이펙트(RENAME)로 인한 키 삭제lazyfree-lazy-flush: 복제 동기화 시 DB 내용 삭제(복제본 FLUSH)lazyfree-lazy-user-del: 사용자 코드에서 DEL 호출을 UNLINK와 동일하게 처리- 기본값은 no. yes로 변경해야 비동기 삭제임
- 바로 삭제 안 되고 메모리에 일시적으로 남아있기 때문에 메모리 사용량 모니터링 필요
- yes여도 내부 알고리즘에 따라 동기가 더 효율적으로 판단되면 동기로 삭제한다.
자료구조 공통 커맨드
- N: 자료구조 내 아이템 개수에 비례해 수행시간 증가한다.
DEL
- string 키 삭제는 O(1)인데 list, set, sorted set, hash는 O(N)
- 오래 걸린다 싶으면 백그라운드 UNLINK 추천
SORT / SORT_RO
- list, set, sorted set에서만 사용 가능
- 키 내부 아이템 정렬해서 반환한다. ASC/DESC/ALPHA 정렬 가능, LIMIT으로 제한 가능
- O(N + Mlog(M))
- N:세트 내 아이템 수, M:반환되는 아이템 수(M)
- STORE(반환값 저장) 옵션 때문에 쓰기 커맨드로 분류돼 복제본에서 못 썼는데 7버전부터
SORT_RO가 추가돼 이걸로 복제본에서 사용 가능
SET
- 집합 연산 커맨드는 집합의 카디널리티에 비례해 수행 시간 증가한다.
- SDIFF, SUNION, SINTER 관련 커맨드 모두 O(N)
- N: 집합 내 아이템 총 개수
- SINTERCARD: O(N*K) (카디널리티 숫자만 반환, 레디스 7버전 이후)
LIST
- 양 끝 인덱스 제외하고는 O(N) 걸림
- LINDEX(인데스 위치의 아이템), LINSERT, LSET(특정 인덱스 아이템 문자열 변경), LPOS(문자열 존재 인덱스 확인)
❓왜 LINDEX는 왜 O(1)이 아니라 O(N)이야? 자바의 List .get()은 O(1)인데
자바는 메모리의 연속적인 공간을 쓰는데 레디스는 링크드 리스트로 포인터로 연결하고 있어서 그럼
HASH
- HGETALL(모든 아이템의 키,값 반환), HKEYS(모든 아이템의 키), HVALS(모든 아이템의 값) 모두 O(N)
SORTED SET
- 데이터가 입력되는 시점에 정렬되므로 O(log(N))
- 아이템 수 증가해도 선형으로 증가하지 않아서 효율적이다.
- 집합 연산할 때는 set처럼 아이템 개수 비례해 증가함
- ZDIFF: O(L + (N-K) log(N))
- L: 모든 집합의 총 아이템 수, N: 첫 번째 세트의 크기, K: 결과 세트의 크기
- ZUNION: O(N) + O(M * log(M))
- N: 모든 집합의 총 아이템 수, M: 결과 집합의 아이템 수
- ZINTER: O(N*K) + O(M * log(M))
- N: 가장 작은 집합의 카디널리티, K: 연산을 수행하는 집합의 수, M: 결과 집합의 카디널리티
- ZINTERCARD: O(N*K) (카디널리티 숫자만 반환, 레디스 7버전 이후)
- ZDIFF: O(L + (N-K) log(N))
레디스 트랜잭션 사용과 주의사항
- 원자적으로 커맨드 보장 가능. 2가지 방식 지원 - 1)MULTI/EXEC 2)루아 스크립트
1) MULTI/EXEC
- MULTI: 트랜잭션 커맨드 시작, EXEC: 트랜잭션 실행
- 트랜잭션 실행 도중 오류 발생 시 모든 커맨드 롤백 후 트랜잭션 종료
> MULTI
OK
(TX)> INCRBY account_balance 100
QUEUED
(TX)> RPUSH tranaction_log "입금 100"
QUEUED
(TX)> EXEC
1) (integer) 100
2) (integer) 1
2) 루아 스크립트
- 가볍고 빠른 임베디드 가능한 스크립트 언어
- 스크립트는 원자적으로 실행되니까 중간에 다른 클라 요청 수행되지 않아서 데이터 일관성 유지 가능
- 일부 명령어 실패해도 다음 명령어 실행되고 롤백은 발생 안 함
SRIPT LOAD로 스크립트 입력하면 hash값 반환됨.EVALSHA로 hash값 실행하면 스크립트 재사용 가능
주의할 점
- 트랜잭션, 루아 실행하는 동안 다른 클라는 대기해야하므로 트랜잭션 길어지지 않게 해야한다.
- 그래서 블로킹 커맨드(BLPOP, BRPOP)은 사용 못 함
has-get/has-del 패턴
- 데이터 조회, 삭제할 때
EXISTS로 먼저 확인하는건 성능이 안 좋다. (테스트 결과 2배 안 좋음) GET은 없으면 nil 반환하고DEL은 키 없어도 에러 반환하지 않으니까EXISTS쓰지 말고 바로 커맨드 날려라
클라이언트 출력 버퍼 사이즈
- 클라 출력 버퍼는 클라한테 응답 반환 전에 일시적으로 저장하는 역할
- 대용량 데이터나 많은 클라 요청 처리해야하면 출력 버퍼 크기 작아 버퍼 오버플로우 발생할 수 있으니 늘리는게 좋다.
(hard limit, soft limit, soft seconds)
client-output-buffer-limit normal 0 0 0: 일반 클라는 출력 버퍼- 제한 비활성화돼있어 제한없이 데이터 수신 가능
client-output-buffer-limit replica 256mb 64mb 60: 복제본에 대한 출력 버퍼- 하드 제한 256MB, 소프트 제한 64MB, 소프트 제한을 초과한 경우 60초 동안 소프트 제한을 계속 초과하면 클라이언트 연결 종료
maxmemory나maxclients설정 확장할 때 복제본 출력 버퍼도 동시에 조정해야한다. (복제양도 늘어나므로)
client-output-buffer-limit pubsub 32mb 8mb 60: pubsub에 대한 출력 버퍼- 해당 조건 만족하면 클라 연결 종료
- pubsub 출력 버퍼 조절하지 않으면 메시지 유실돼 복구 불가됨 (키스페이스 알림 기능, 아래 케이스)
레디스 키스페이스 알림 기능
- 키 변경 사항 모니터링 해 pubsub 채널로 변경 사항 메시지 구독하는 기능임
notify-keyspace-events설정 파일에서 어떤 이벤트 받을 건지 설정하면 키 만료는__keyevent@0__:expired채널로 이벤트 온다.- 근데 많은 키가 만료돼 메시지가 대량 발송돼 pubsub 버퍼 크기를 초과하면 유실되버림
- fire-and-forget 패턴이라 클라 출력 버퍼 초과돼서 한번 끊겼다 재연결되면 그 사이에 발행된 메시지는 확인 불가됨
- 해결: pubsub 버퍼 크기 늘리기, 여러 레디스 인스턴스로 pubsub 이벤트 분산시키기
특정 프리픽스 가진 키 삭제하기
- 특정 프리픽스만 삭제하는 레디스 기능은 없음
- 프리픽스 삭제하는 법
- ❌ prefix pattern 만족하는 키 SCAN으로 조회해 끝날 때까지 모으고 for문으로 키 DEL 하기
- SCAN할 때 네트워크 통신 반복적으로 걸리고 DEL 할 때도 반복적으로 왕복시간 소요돼서 느림
-
pattern = 'prefix:*' count = 100 cursor, keys = 0, []
- ❌ prefix pattern 만족하는 키 SCAN으로 조회해 끝날 때까지 모으고 for문으로 키 DEL 하기
SCAN 명령을 사용해 특정 프리픽스를 가진 키 검색
while True:
cursor, partial_keys = r.scan(cursor, match=pattern)
keys.extend(partial_keys)
if cursor == 0:
break
검색된 키 삭제
for key in keys:
r.delete(key)
<br>
- ✅ (최소한의 영향) 루아 스크립트
- SCAN단위로 네트워크 통신하기 때문에 네트워크 I/O 최소화
- 대신 한번작업 너무 오래 안 걸리게(다른 클라 대기타므로) 배치 수 조절
- ```java
# 삭제할 키의 패턴 및 스캔 파라미터 설정
pattern = 'prefix:*'
count = 100
cursor = "0"
# 루아 스크립트 정의
lua_script = """
local cursor = ARGV[1]
local pattern - ARGV[2]
local count = ARGV[3]
local keys = redis.call("SCAN", cursor, "MATCH", pattern, "COUNT", count)
local cursor = keys[1]
local keyList = keys[2]
for _, key in ipairs(keyList) do
redis.call("DEL", key)
end
return {cursor, #keyList}
"""
total_deleted_count = 0
# 반복적으로 루아 스크립트를 호출해 키를 조회하고 삭제
while True:
result = r.eval(lua_script, 0, cursor, pattern, count)
cursor, deleted_count = result[0], result[1]
total_deleted_count += deleted_count
if cursor == "0":
break
레디스 모니터링
슬로우 로그
> SLOWLOG GET
1) 1) (integer) 1923
2) (integer) 1696344048
3) (integer) 35233
4) 1) "SCAN"
2) "10179327"
3) "COUNT"
4) "50000"
- 슬로우로그 고유 ID
- 실행 시간(timestamp): 명령이 실행된 시간
- 실행 시간(마이크로초): 명령이 실행되는 데 소요된 시간 (독스, The amount of time needed for its execution, in microseconds)
- 명령: 느리게 수행됐던 커맨드
slowlog-log-slower-than
127.0.0.1:6379> CONFIG GET slowlog-log-slower-than
1) "slowlog-log-slower-than"
2) "10000"
디폴트: 10000마이크로초(10ms) 넘은 커맨드를 기록
slowlog-max-len
127.0.0.1:6379> CONFIG GET slowlog-max-len
1) "slowlog-max-len"
2) "128"
기본: 최대 128개 슬로우로그만 유지
그래프 지표
CPU
- 백업 파일 저장이나 UNLINK같은 백그라운드 작업에 다른 CPU 활용할 수 있으니 부하 모니터링 필요
- 시간 복잡도 높은 커맨드, 카디널리티가 높은 집합 자료 구조, 많은 아이템이 저장된 단일 hash면 CPU 영향 준다.
메모리
used_memory: 레디스가 현재 할당한 메모리ByteUsedForCache: 현재 메모리 사용량DatabaseMemoryUsagePercentage: maxmemory 중 백분율 사용량- 100이면 정책에 따라 이빅션 실행 가능
used_memory는 레디스가 논리적으로 사용하는 메모리,used_memory_rss(Resident Set Size)는 OS가 레디스 프로세스에 할당한 실제 물리적인 메모리-
지표 의미 비유 used_memory레디스가 데이터를 저장하기 위해 실제로 점유한 데이터의 양 (논리적) 책꽂이에 꽂힌 '책들의 실제 부피' used_memory_rssOS가 레디스 프로세스에 실제로 할당해 준 총 메모리 양 (물리적) 책들을 꽂기 위해 차지하고 있는 '책장 전체의 공간' - 논리적 메모리에 비해 물리적으로 사용하는 메모리가 너무 커질 때 메모리 단편화 문제 발생 가능
-
메모리 단편화(Fragmentation)란?
메모리 단편화는 **"레디스는 데이터를 지웠다고 하는데, OS는 그 공간을 아직 돌려받지 못한 상태"**에서 주로 발생합니다.
-
-
- 발생 원인: 레디스가 데이터를 삭제하면 해당 메모리 공간은 '빈 상태'가 됩니다. 하지만 OS의 메모리 할당자(Allocator, 주로 jemalloc 사용)는 이 빈 공간이 너무 작거나 흩어져 있으면 다른 프로세스에게 돌려주지 않고 레디스가 나중에 다시 쓰도록 그대로 둡니다.
- 문제 상황: 실제 데이터(
used_memory)는 1GB뿐인데, OS가 할당한 메모리(used_memory_rss)는 2GB라면? 중간에 **1GB만큼의 쓸모없는 빈틈(단편화)**이 생긴 것입니다.
- 메모리 효율성이 떨어진다. 단편화가 문제가 된다면
CONFIG SET activedefrag yes활성화할 수 있음(버전 4이상), 문제 없으면 안 해도됨
-
네트워크
- 레디스는 더 많은 명령 처리할 수 있는데 네트워크 RTT 때문에 처리량 낮을 수 있음
- 네트워크 지표가 특정 지점에서 상승하지 않으면 네트워크 임계치에 걸려 병목 현상인지 확인해라
- 네트워크 사용량 증가 원인이
- 읽기 작업이면: 복제본 활용하게 해야함, 이미 그러면 추가 복제본 구성해 분산시키기
- 쓰기 작업이면: 서버 사양 업그레이드. 아님 클러스터 모드로 쓰기 작업을 여러 노드로 분산시키기
커넥션
- 클라 연결 추이 모니터링
- 일반적인 수준과 갑자기 다르게 활성 연결 수 증가하면 클라 연결 비정상 종료돼 유지되고 있는건 아닌지 확인 필요
tcp-keepalive설정으로 유휴 연결 끊게 할 수 있음 (기본: 300초마다 연결 유효성 확인, 문제 있으면 끊음)- 클라를 새롭게 연결하는건 상당한 비용이 드니 앺은 기존 연결 재사용하기 위해 커넥션 풀링 사용하는게 좋다.
1. 왜 새롭게 연결하는 것이 '비싼 비용'인가요?
레디스와 앱이 처음 연결될 때는 다음과 같은 복잡한 단계(Handshake)를 거칩니다.
TCP 3-Way Handshake: 네트워크상에서 서로 통신할 준비가 됐는지 3번 확인합니다.
인증(AUTH): 패스워드가 있다면 확인하는 절차가 추가됩니다.
프로토콜 설정: 레디스와 데이터를 주고받을 형식을 맞춥니다.
이 과정은 실제 데이터를 주고받는 시간보다 준비 운동(Overhead) 시간이 더 걸릴 정도로 리소스를 많이 잡아먹습니다. 초당 수만 건의 요청이 들어오는 환경에서 매번 이 짓을 한다면 서버는 금방 지쳐버리겠죠.
2. 커넥션 풀링(Connection Pooling)의 원리
커넥션 풀은 미리 **'연결된 통로'**들을 만들어 놓고 커다란 바구니(Pool)에 담아두는 개념입니다.
요청 발생: 앱이 레디스에 데이터를 넣으려 할 때, 새로 연결하지 않고 바구니에서 노는(Idle) 연결을 하나 꺼내 씁니다.
작업 완료: 데이터를 다 보냈으면 연결을 끊는(Close) 게 아니라, 다시 바구니에 반납합니다.
재사용: 다음번 요청이 들어오면 방금 반납된 그 연결을 그대로 다시 꺼내 씁니다
-
Lettuce는 "많은 요청을 하나의 연결에 태우기" 위해 멀티플렉싱(정확히는 비동기 I/O 공유)을 쓰는것이라 커넥션 풀링 굳이 필요 없음
- TLS 통신일 경우 TLS 핸드셰이크가 많은 시간과 CPU 소모되니 새로운 연결 개수 제어하는게 좋다.
복제
- 복제본이 마스터 전송 속도 못 따라잡는 복지 지연이 있는지 모니터링
- 복제 지연이 길어지면 복제본이 전체 동기화 요청해야할 수도 있어서 그럼 마스터 노드에서 스냅샷 생성해야해서 성능 저하 초래함
- 복제 지연 원인: 많은 쓰기 작엄, 네트워크 대역폭 고갈, 복제 출력 버퍼 크기
'SearchDeveloper > 개발자를 위한 레디스' 카테고리의 다른 글
| 12장 클라이언트 관리 (0) | 2026.06.07 |
|---|---|
| 11장 보안 (0) | 2026.06.07 |
| 10장 클러스터 (0) | 2026.06.07 |
| 9장 센티널 (0) | 2026.06.07 |
| 8장 복제 (1) | 2026.06.07 |