DBA를 위한 강의는 아니지만 레디스가 워낙 성능, 용량, 세팅에 민감한 프로덕트이고 실제 서비스 성능에도 영향을 많이 미치기 때문에 어플리케이션 개발자라고 하더라도 레디스를 이런 식으로 운영할 수 있구나!! 라는 것을 알고 있어야 제대로 된 키 설계와 사용법을 설계할 수 있다.
운영과 관련되 내용을 다루긴 하지만 사실상 레디스를 잘 쓰기 위해서 어플리케이션 개발자들도 모두 알아야 하는 내용이다.
Redis는 In-Memory Database이다. Memory는 가격이 비싸고, 한 Node당 가질 수 있는 용량이 Disk에 비해서 한계가 있으므로 용량의 관리가 중요하다. Memory에 저장된 데이터는 컴퓨터가 종료되면 사라지므로(volatile), 정상/비정상 종료가 되더라도 데이터가 유지되도록 하는 방법을 고민할 뿐만 아니라 비정상 종료가 되지 않도록 Node를 관리하는 것이 중요하다.
Memory를 관리하는 가장 기초적인 방법은 maxmemory를 넘지 않도록 하는 것이다.
maxmemory에 다다르면 새로운 데이터 저장을 위해서 기존에 저장된 데이터를 제거해야 한다. 이때 제거되는 데이터를 Key라고 부르며, 어떤 Key를 제거할지를 결정하는 전략을 Key Eviction(키 제거 정책) 이라고 한다.
redis.conf 파일에 maxmemory
키로 max memory를 설정한다.
Redis가 관리하는 memory는 추정치이다. maxmemory는 해당 노드에 Redis가 가용한 총 memory보다 조금 적게 주어야 오차가 있더라도 관리가 된다.
client가 새로운 command를 요청하고, 그 결과 새로운 데이터가 추가된다면 다음 2과정으로 넘어간다.
memory usage를 확인하고 maxmemory limit을 넘는지 확인한다. 넘는다면, eviction policy에 따라서 key를 eviction한다.
새로운 command를 실행한다.
conf 파일에 maxmemory-policy
키로 설정할 수 있다.
noeviction
memory limit에 다다르면, 새로운 값을 저장할 수 없다. (client의 command는 error를 리턴받는다.) replication을 적용하는 경우, 이 설정은 primary database에만 적용 가능하다.
allkeys-lru
모든 key 중에서 가장 최근에 사용된 시점이 오래된 key를 제거한다.
여기서 LRU는 Least Recently Used, 즉 가장 최근에 사용된 시점이 오래된 순을 뜻한다.
expire 설정 여부와 상관없이 모든 key가 대상이다.
allkeys-lfu
volatile-lru
LRU
정책과 동일하되, expire가 설정(true)된 key만 대상으로 LRU를 적용한다.volatile-lfu
LFU
정책과 동일하되, expire가 설정(true)된 key만 대상으로 LFU를 적용한다.
allkeys-random
모든 key 중에서 무작위(Random) 로 key를 선택해 제거한다.
expire 설정 여부와는 관계 없다.
volatile-random
volatile-ttl
**expire가 설정(true)됐다 : 사용자가 얘는 지워져도 돼. 라고 의사를 표현한 걸로 본다.
Eviction policy는 Redis를 껐다 켜지 않아도 실행 중에 바로 바꿀 수 있다. (runtime에 변경 가능)
INFO
의 결과 cache misses 수가 증가하는 것을 보고 수정의 근거를 찾는 것이 바람직하다.When: Application 개발자들이 어떻게 사용할지 모르고, expire를 설정하는 것의 가이드가 잘 되어있지 않다면
When: Application 개발자들이 어떻게 사용할지 모르고, expire 설정하는 것의 가이드가 잘 되어있지 않은데, Key의 분배가 고르고 사용 패턴이 일정하다면
(결국 랜덤하게 고르게 지워질 테니, 예를들어 사람들이 오후 12에 가장 많이 사용되고, 5시, 6시에는 잘 사용되지 않는데 MAXmemory 도달한 시점이 5~6시 였다. 그러면 12시에 가장 많이 사용했던 키들이 지워질 수 있다. (LRU 쓴다고 하면 ) allkeys-random 이라고 하면 12시든 5시즘 플럭트리션(flucktration)이 규칙이라면 전체적으로 고르게 비슷한 수의 키들이 지워질 테니 어플리케이션도 비슷한 확률로 키가 미스될 것이다. 이런 가정으로
allkeys-random를 사용하는 것이다.
When: Application 개발자들이 어떻게 사용할지 모르지만, expire가 대부분 설정이 되어있다면
결론적으로 할 수 만 있다면, volatile-ttl 이 Redis 사용자(client)의 의도를 해치지 않는 가장 안정적인 선택이다.
key에 expire 를 설정하지 않으면 volatile-lru
, volatile-lfu
, volatile-random
, volatile-ttl
의 동작은 noeviction 과 같다. 이는 Redis (cluster)의 상태를 안좋게 만들수 있는 위험이 있다.
따라서 모든 redis의 key에는 expire 설정을 꼭 하는 것이 필요하다.
(List, HLL등의 자료구조에서 key 안의) member의 경우 expire 설정을 할 수 없다.
(Enterprise 는 가능) 이때 활용할 수 있는 디자인 패턴으로는 다음과 같은 방법을 생각해볼 수 있다.
결론: 가장 안정적인 volatile-ttl 를 선택할 수 있도록, expire를 항상 설정하도록 설계하고, 사용하자.
Redis는 데이터를 메모리 뿐만 아니라 File로도 남겨서 메모리의 데이터가 사라졌을 때 복
구할 수 있는 방법을 제공한다.
아래와 같은 옵션이 있다.
주기적으로 해당 시점의 snapshot을 남긴다.
모든 write operation 마다 Log를 쌓는다. 처음부터 모두 실행하면 데이터가 모두 복구된다. 해당 File은 append만으로 쓰여진다. Logging되는 데이터 자체가 redis protocol과 일치해서 AOF로부터의 복구가 쉽다. File이 너무 커질경우를 위해서 rewrite 기능으로 파일이 너무 커지지 않도록 관리한다.
appendfsync always | everysec | no
같은 설정으로 동작 방식 조절 가능.AOF에는 다음과 같은 fsync 옵션들이 있다. 자신이 원하는 안정성(durability)에 따라서 속도와의 trade-off를 고려해서 선택한다.
데이터를 파일로 남가지 않는다. 성능의 손해가 없다.
RDB와 AOF를 조합한 방식.
RDB의 주기 사이는 AOF로 중간과정을 남긴다.
Persistence의 성능의 병목지점은 Disk이다. 때문에 Disk는 HDD보다는 SSD를 쓰는 것이좋다. (사실 거의 무조건 SSD를 써야한다.)
성능을 위해서는 Persistence를 쓰지 않고 Replication(데이터의 복제)을 이용해서 유실되지 않는 정도로 보장하는 것이 좋다. (물론, Replication은 persistence 처럼 파일수준의 복구용으로 보장되지 않기 때문에 목적이 다르다.) 중요한 데이터의 경우 Application에서 Redis Cache에 hit되지 않으면 원본데이터(RDBMS 등)을 조회하도록 구현해놓는 것은 필수이다.
우리가 풀고있는 문제중, 다음 3가지의 옵션 중 어떤 것을 사용하는 것이 좋을까 고민해보자.
성능의 지속적인 손해가 있음에도 AOF로 Persistence를 쓰는 것
Cache의 복구에 불편함과 불완전함이 있지만 RDB로 Persistence를 써서 성능의 손해를 최소화 하는 것
장애로부터의 복구에 불편함이나 손해가 있더라도 Persistence를 쓰지 않고 성능의 이
점을 극대화 하는 것
장점:
거의 모든 쓰기 연산이 디스크에 기록되므로, 재시작 시 데이터 유실 가능성이 매우 낮음.
운영 중 장애가 발생해도 정확하게 상태 복구가 가능.
실시간성 요구가 높고 데이터 손실이 치명적인 서비스에 유리 (예: 금융, 주문 시스템 등).
단점:
매 쓰기마다 파일 기록이 발생 → 디스크 I/O 증가, 성능 저하.
AOF 파일이 커지면 Redis 시작 시 시간이 오래 걸림 (rewrite 필요).
설정에 따라 write latency에 영향을 줄 수 있음 (appendfsync always는 성능 최악, everysec은 절충안).
사용 추천 시나리오:
데이터 무결성이 최우선이고, 약간의 성능 손해는 감수할 수 있는 경우
사용자 행동 로그 저장, 주문 기록 저장 등 비즈니스 핵심 데이터 처리
장점:
RDB는 백그라운드로 덤프를 뜨기 때문에 성능에 거의 영향 없음.
Redis 서버 재시작 시 빠르게 로드 가능 (AOF보다 부하 적음).
디스크 공간 사용량도 작음.
단점:
스냅샷 주기 사이의 데이터는 복구되지 않음 → 유실 가능성 존재.
실시간성이나 완전 복구가 필요한 서비스에는 부적합.
RDB dump가 수행되는 타이밍에 따라 복구 시점의 시차 발생
사용 추천 시나리오:
Redis를 단순 캐시로 사용하는 경우 (복구보다는 성능 우선)
일시적 저장소 역할, 원본 데이터는 RDBMS 등에 있고 Redis는 서브 캐시인 경우
장점:
Persistence가 없기 때문에 디스크 I/O가 전혀 없음 → 최고 성능 확보 가능
Redis 본연의 빠른 캐시 성능 극대화
메모리 기반 처리의 지연 시간 최소화
단점:
장애 시 데이터 전부 유실
복구 시간이나 상태 복원 불가능, 클러스터 구성도 무력화될 수 있음
완전한 비휘발성 요구하는 경우엔 부적합
사용 추천 시나리오:
Redis를 pure cache (L1 캐시)로 사용하는 경우
애플리케이션 레벨에서 복구나 리로드가 가능한 경우 (ex: DB에서 다시 로드)
데이터 손실이 치명적이지 않은 로그, 세션, TTL 캐시 등