TTL이란?

TTL(Time To Live)은 Redis 키에 수명을 부여하는 기능이다. 설정된 시간이 지나면 키가 자동으로 삭제된다.

SET session:user123 "data"
EXPIRE session:user123 3600      # 3600초(1시간) 후 만료

또는 한번에:

SET session:user123 "data" EX 3600

관련 명령어

명령어 설명
EXPIRE key seconds 초 단위 TTL 설정
PEXPIRE key ms 밀리초 단위 TTL 설정
EXPIREAT key timestamp Unix 타임스탬프로 만료 시점 지정
TTL key 남은 TTL 확인 (초)
PTTL key 남은 TTL 확인 (밀리초)
PERSIST key TTL 제거 (영구 키로 전환)

TTL 반환값

TTL mykey
# -1 : 키가 존재하지만 TTL이 없음 (영구)
# -2 : 키가 존재하지 않음
# 양수 : 남은 초

만료 처리 전략

Redis는 키 만료를 두 가지 전략으로 처리한다.

1. Passive Expiration (수동 만료)

클라이언트가 키에 접근할 때 만료 여부를 확인한다.

Client: GET session:user123
Redis:  (내부) 만료 시간 확인 → 지남 → 삭제 → nil 반환
  • 장점: CPU 부하 없음
  • 단점: 아무도 접근하지 않는 키는 영원히 메모리에 남음

2. Active Expiration (능동 만료)

Redis가 주기적으로(초당 10회) 만료된 키를 찾아 삭제한다.

동작 방식:

매 100ms마다:
1. 만료 시간이 설정된 키 중 랜덤으로 20개 샘플링
2. 샘플 중 만료된 키 삭제
3. 만료된 키가 25% 이상이면 → 즉시 1번부터 반복
4. 25% 미만이면 → 다음 주기까지 대기

핵심: 만료 키가 많으면 적극적으로 정리하고, 적으면 느긋하게 동작한다. 이를 통해 CPU 사용량과 메모리 낭비 사이에서 균형을 맞춘다.

두 전략의 조합

┌──────────────────────────────────┐
│           Redis 키 만료          │
├──────────────┬───────────────────┤
│  Passive     │  Active           │
│  (접근 시)    │  (백그라운드)      │
│              │                   │
│  100% 정확   │  확률적 샘플링     │
│  CPU 0       │  CPU 약간 사용    │
│  누수 가능    │  누수 방지        │
└──────────────┴───────────────────┘
         ↓ 조합하면 ↓
   효율적이면서 메모리 누수 최소화

내부 구현: redisDb 구조

Redis는 내부적으로 두 개의 딕셔너리를 관리한다.

typedef struct redisDb {
    dict *dict;     // 모든 키-값 저장
    dict *expires;  // TTL이 설정된 키 → 만료 시각 매핑
} redisDb;

EXPIRE key 60 을 실행하면:

  1. expires 딕셔너리에 key → (현재시각 + 60초) 를 저장
  2. dict에는 변화 없음

만료 확인 시:

  1. expires에서 키의 만료 시각을 조회
  2. 현재 시각과 비교
  3. 지났으면 dictexpires 양쪽에서 삭제

TTL과 메모리 정책 (Eviction)

TTL 만료와 메모리 초과 시 퇴거(eviction)는 별개 메커니즘이다.

maxmemory-policy 옵션

maxmemory 한도에 도달했을 때 어떤 키를 제거할지 결정한다.

정책 설명
noeviction 쓰기 거부 (에러 반환)
allkeys-lru 모든 키 중 LRU 제거
volatile-lru TTL 있는 키만 LRU 제거
allkeys-lfu 모든 키 중 LFU 제거 (Redis 4.0+)
volatile-lfu TTL 있는 키만 LFU 제거
allkeys-random 랜덤 제거
volatile-random TTL 있는 키 중 랜덤 제거
volatile-ttl TTL이 가장 짧은 키 먼저 제거

실무 권장

# redis.conf
maxmemory 4gb
maxmemory-policy allkeys-lfu
  • 캐시 용도: allkeys-lru 또는 allkeys-lfu
  • 세션 저장소: volatile-lru (TTL 없는 키는 보호)

TTL 관련 주의사항

1. RENAME과 TTL

SET a "hello" EX 100
SET b "world"
RENAME a b
TTL b   # → 100 (a의 TTL이 b로 이전됨)

RENAME하면 TTL이 따라간다. 대상 키(b)에 TTL이 있었든 없었든, 원본(a)의 TTL로 덮어씌워진다.

2. SET은 TTL을 제거한다

SET mykey "v1" EX 100
SET mykey "v2"          # TTL 사라짐!
TTL mykey                # → -1

SET은 기존 키를 완전히 덮어쓰므로 TTL이 제거된다. 값만 변경하려면 SET mykey "v2" KEEPTTL (Redis 6.0+)을 사용한다.

3. INCR, LPUSH 등은 TTL에 영향 없음

SET counter 0 EX 100
INCR counter
TTL counter   # → 여전히 약 100 (보존됨)

키의 값을 변경하는 명령어는 TTL을 유지한다. 키 자체를 재생성하는 명령어(SET, GETSET 등)만 TTL에 영향을 준다.

4. RDB/AOF에서의 TTL

  • RDB: 스냅샷 저장 시 만료 시각도 함께 저장. 로딩 시 이미 만료된 키는 무시
  • AOF: EXPIREAT(절대 시각) 형태로 기록. 재시작 시 정확한 만료 처리 가능

실무 활용 패턴

세션 관리

SET session:abc123 "{userId:1, role:admin}" EX 1800
# 사용자 활동 시 갱신
EXPIRE session:abc123 1800

캐시 + TTL

SET cache:product:100 "{name:...}" EX 300
# 5분 캐시, 만료 후 DB 재조회

Rate Limiting

SET ratelimit:user:123 1 EX 60 NX   # 1분 윈도우 시작
INCR ratelimit:user:123              # 요청마다 증가
# 값이 100 초과 시 → 차단

정리

항목 핵심
만료 방식 Passive(접근 시) + Active(백그라운드 샘플링)
내부 구조 expires 딕셔너리에 만료 시각 저장
SET 주의 SET은 TTL 제거 → KEEPTTL 사용
메모리 초과 TTL 만료와 별개로 maxmemory-policy가 동작
복제 환경 마스터에서 만료 → 레플리카에 DEL 전파

카테고리:

업데이트: