🔎 Overview
최근 레디스에 대한 개념을 확실하게 이해하고 개발에 사용하기 위해서, 실전 레디스 : 기초, 실전, 고급 단계별로 배우는 레디스 핵심 가이드 도서를 읽으며 내용을 이해하고, 실습하고 있다.
여러 개념들을 알아가던 중, 굉장히 눈에띄는 문구가 있었다.
레디스 최악의 사례 7가지
이 얼마나 폭력적인 문장인가. 딱 봐도 레디스 기술 사용에 있어서 굉장히 중요한 요소임을 강조하는 듯하다.
그런 의미에서, 해당 내용을 블로그에 정리해두기로 결정했다.
1️⃣ 비밀번호 미설정
- 많은 경우, 레디스에서 비밀번호를 설정하지 않음
- 비밀번호를 따로 설정하지 않는다면, 해당 레디스에 접속한 어떤 사람이든지 레디스 명령어 사용 가능
- 레디스 설정을 바꾸거나, 데이터 삭제, 디도스 공격 등 일어나서는 안 되는 일들이 벌어질 수 있음
- 해킹에 매우 취약
💡 반드시 비밀번호를 설정하자
requirepass {passwd}
- 사용하는
conf 설정 파일에서 requirepass 를 반드시 지정
- 해당 비밀번호를 통해 인증해야만, 레디스 명령어 사용 가능
AUTH {passwd}
redis-cli 등 클라이언트에서 명령어를 실행하기 위해서는, 반드시 AUTH 를 통해 인증이 완료되어야 함
2️⃣ KEYS 명령어 사용 금지
KEYS {pattern} : 레디스 서버에서 사용 중인 키들을 전부 확인하는 명령어
- 키의 개수가 많아지면 많아질수록 성능에 큰 영향
🤔 WHY
- 레디스는 기본적으로 단일 스레드 구조
KEYS 의 시간복잡도는 O(N)
- 최대 2의 32승개의 키를 저장 가능
- 운영 환경에서 잘못 사용한다면, 한 번의
KEYS 명령으로 레디스 서버 전체가 몇 초 ~ 수십 초간 블로킹될 가능성 존재
💡 SCAN 명령어를 사용하자
SCAN 0 MATCH user:* COUNT 100
SCAN 명령어는 기본적으로 비동기적 조회
SCAN 명령어와 인덱스, 카운트 개수를 여러 번 호출하여, 전체 키를 천천히 순회
- 배치 처리와 유사
3️⃣ SELECT - 번호 기반 DB 사용
SELECT {번호} 명령을 사용하면 레디스 내의 각자 다른 데이터베이스를 사용할 수 있음
- ex)
SELECT 0 , SELECT 1
- 0-based-index
- 하지만 이런 번호 기반 데이터베이스들은 완전 부리된 데이터베이스가 아님
🤔 WHY
- 같은 키:값이 DB 0, DB 1에서 각각 별도로 존재할 수 있지만, 내부적으로는 동일한 레디스 인스턴스 자원(CPU, 메모리, 이벤트 루프 등)을 공유
- DB 0에서
KEYS 명령을 사용하면, 이 명령은 레디스 인스턴스의 이벤트 루프를 블로킹하므로, 같은 인스턴스 내 데이터베이스 또한 모두 블로킹
- 즉, 독립적인 것처럼 보여도 실제 격리/스케일링 측면에서는 전혀 독립적이지 않음
- 기술적인 면에서 또한, 클러스터 환경에서는 번호 기반 데이터베이스 미지원
- 즉, 확장성, 안정성 측면에서 심각한 제약 초래
- 레디스 창시자 Salvatore Sanfilippo 또한, "레디스에서 만든 최악의 디자인 실수" 라고 평가할 정도
💡 번호 기반 DB 대신, 레디스 인스턴스를 새로 띄워 분리하여 운영하자
- 레디스는 인스턴스 하나당 메모리 오버헤드가 낮음
- 따라서 레디스 서버를 따로 띄워 인스턴스를 추가하고 클러스터 버스라는 내부 채널 안에서 노드 간 통신을 하는 레디스 클러스터 방식을 사용하면, 샤딩을 통한 데이터 분리 및
MOVED 명령을 통한 해당 데이터 소유 캐시 노드로의 자동 리다이렉션 또한 가능
4️⃣ HGETALL, LRANGE 같은 무제한 반환 주의
- 레디스 명령어 중 일부는 모든 데이터를 한 번에 반환
HGETALL : Hash 자료형의 모든 field/value 반환
LRANGE 0 -1 : List 자료형의 모든 요소 반환
- 레디스에서 오프셋에 사용되는
0 은 시작 인덱스이며, -1 은 마지막 인덱스
SMEMBERS : Set 자료형의 모든 요소 반환
ZRANGE 0 -1 : Sorted Set 자료형의 모든 요소 반환
- etc)...
🤔 WHY
- 매우 큰 데이터 구조
- Hash : 필드 수 최대 2의 32승, 값 최대 512MB
- List : 요소 수 최대 2의 32승, 값 최대 512MB
- Set / Sorted Set : 요소 수 매우 많을 수 있음
- 사용 데이터가 누적된다면 기존 예상보다 훨씬 큰 데이터 구조가 될 가능성 존재
- 이러한 데이터를 한꺼번에 모두 가져온다면, 최악의 경우레디스 서버 자체가 다운
💡 크기를 먼저 체크한 후, 필요한 범위 내에서만 조회하자
- 데이터 크기 확인
- Hash :
HLEN {key}
- List :
LLEN {key}
- Set :
SCARD {key}
- Sorted Set :
ZCARD {key}
- 필요한 만큼만 부분 조회
- List :
LRANGE {startIdx} {endIdx}
- 그 외 :
{Xxx}SCAN ...
5️⃣ 하나의 커넥션 당 하나의 명령은 비효율적
- 대부분의 네트워크 통신 사용 기술에서 공통적으로 명시되는 명제
- 레디스는 지속 연결 활용을 전제로 설계
- 레디스 클러스터 사용 시 :
- OSS Cluster API -> 클라이언트가 노드별 연결 유지
- Redis Enterprise -> 프록시를 통한 연결 관리, 클러스터 복잡성은 숨김 처리
- 매 연결 생성 및 해제 시 오버헤드 발생
💡 지속 연결을 활용하자
- 연결은 열어둔 채 여러 명령을 수행
- 필요 시 커넥션 풀 활용
- REST 스타일 연결 패턴은 피할 것
6️⃣ Hot Keys 문제
- 레디스는 자주 접근되는 핵심 데이터를 저장할 때 효율 상승
- 그렇지만 특정 몇 개의 키만 반복해서 접근하게 되면 Hot Keys 문제 발생
🤔 WHY
- 레디스 클러스터 구조에서는 키가 데이터 저장 위치를 결정
- 키 해싱을 통해 슬롯 위치를 판단하고, 해당 슬롯을 어느 샤드에 분배할지 결정
- 하나의 키에 요청 집중 시 :
- 해당 특정 노드에 모든 트래픽 집중
- 클러스터 내의 나머지 노드는 미활용
- 병목 현상 발생
💡 작은 수의 자주 쓰는 키 집중을 피하자
- 설계 단계에서 Hot Key 패턴을 방지
- 키 분산 설계
- 동일 데이터를 여러 키에 분산 저장
- 즉, 서로 다른 샤드에 배치
- etc)...
7️⃣ 레디스를 주 DB 로 사용할 때 주의
- 레디스를 주 DB로 사용하는 경우, 다운되면 전체 애플리케이션 중단
- 단순한 레디스 기본 설정으로는 안정적인 주 DB 역할은 기대하기 어려움
💡 적절한 HA와 Durability를 설정하자
- 고가용성 (High Availability)
- OSS Redis -> Redis Sentinel 설정
- 모니터링을 통해 마스터 서버 정상 연결 유무 확인
- 페일오버를 통해 마스터 서버 장애 시 레플리카를 마스터로 자동 승격
- 클라이언트에게 승격된 마스터 IP를 마스터 서버로 알림
- 이러한 과정을 통해, 한 대의 레디스 서버가 죽더라도 서비스 미중단
- 영속성 (Durability / Persistence)
- OSS Redis -> RDB 스냅샷 또는 AOF 파일 사용
- 해당 파일들이 일종의 백업 DB 파일
- 레디스는 기본적으로 인-메모리 방식이므로 휘발성 메모리(RAM)를 사용하지만, 해당 파일들로부터 데이터를 불러오는 것이 가능
📌 Redis Enterprise 를 사용하는 경우, 별도의 설정 없이도 멀티 노드 클러스터, 복구, 백업이 모두 자동화되므로 바로 주 DB로 사용 가능
Reference :