백엔드 데이터 인프라 56편 — Redis Keyspace Notifications + 세션 만료 패턴

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 56편. Redis Keyspace Notifications — 키 만료·변경·삭제 이벤트를 Pub/Sub 으로 받는 메커니즘. notify-keyspace-events 설정 flag·__keyspace__·__keyevent__ 채널·세션 만료 후속 처리 패턴까지 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 56편 — Redis Keyspace Notifications + 세션 만료 패턴

이 글은 백엔드 데이터 인프라 시리즈 130편 중 56편이에요. 55편 에서 TTL(키 수명) · eviction policy(메모리 부족 시 키 강제 삭제 규칙) 를 잡았다면, 이번 56편은 Keyspace Notifications키에 일어난 일을 Pub/Sub(발행/구독 메시지 모델) 으로 받는 메커니즘이에요. "세션 만료될 때 알림 받고 후속 처리 자동 실행" 같은 반응형 패턴의 빌딩 블록.

Keyspace Notifications가 어렵게 느껴지는 이유

이름이 길고, 설정 flag 가 알파벳 한 글자씩 묶여 처음 보면 암호 같아요.

첫째, 두 가지 채널 모델이 헷갈립니다. __keyspace__@<db>__:<key>__keyevent__@<db>__:<event> — 이름이 비슷한데 어느 쪽을 구독해야 하는지 가 안 잡혀요. "키 하나에 일어난 일을 본다""이벤트 타입별로 본다" 의 차이.

둘째, notify-keyspace-events 설정이 알파벳 조합. KEA·Ex·AKE 같이 flag 문자열 로 켜는데, 어느 글자가 어떤 이벤트인지 매번 외울 수가 없어요. 표 하나만 옆에 두면 끝나는 영역이지만 처음에는 마법주문 처럼 보이죠.

셋째, 기본값이 꺼져 있음 입니다. 설치 직후에는 notify-keyspace-events "" 라서 아무 이벤트도 발송 안 됨. "Redis 코드 박았는데 알림이 안 오는데요?" 의 가장 흔한 원인 1순위.

이 글에서 두 채널 모델·flag 표·세션 만료 패턴까지 한 번에 정리해요.

두 가지 채널 모델

같은 이벤트를 두 가지 다른 각도 로 발송해요. 어느 쪽을 구독할지는 내 관점이 키 중심이냐 이벤트 중심이냐 에 달려 있어요.

__keyspace__@<db>__:<key> — 키 중심

특정 에 일어난 모든 이벤트 를 받아요. 채널 이름에 키 이름 이 박혀요.

# user42 의 세션 키에서 일어나는 모든 이벤트 구독
> SUBSCRIBE "__keyspace__@0__:session:user42"

받는 메시지 = 명령 이름 (set·del·expire·expired 등).

언제 쓰나 — 특정 키 하나에만 관심 이 있을 때.

__keyevent__@<db>__:<event> — 이벤트 중심

특정 이벤트 타입모든 키 를 받아요. 채널 이름에 이벤트 이름 이 박혀요.

# 만료된 모든 키 구독
> SUBSCRIBE "__keyevent__@0__:expired"

받는 메시지 = 키 이름 (만료된 키 이름이 메시지로 옴).

언제 쓰나 — "모든 만료 이벤트를 잡아 후속 처리" 같은 광역 모니터링.

어느 쪽을 쓸까

대부분 __keyevent__ 가 자주 쓰여요. 세션 만료 후속 처리·캐시 무효화·삭제 트래커 같이 이벤트 타입 한 가지 를 광역으로 봐야 하는 자리가 많아서.

__keyspace__특정 키 하나에 다양한 일이 일어나는지 보는 디버깅·모니터링 자리에 자주 써요.

한 줄 정리 — keyspace = 키 한 개의 모든 이벤트 / keyevent = 이벤트 한 개의 모든 키.

notify-keyspace-events 설정 flag 표

이 표 하나만 옆에 두면 끝나요.

flag 의미
K __keyspace__ 채널 발송 (키 중심)
E __keyevent__ 채널 발송 (이벤트 중심)
g Generic 명령 (DEL·UNLINK·RENAME 등)
$ String 명령 (SET·APPEND 등)
l List 명령 (LPUSH·RPOP 등)
s Set 명령 (SADD·SREM 등)
h Hash 명령 (HSET·HDEL 등)
z Sorted Set 명령
x Stream 명령
e 만료 이벤트(expired) ← 가장 자주 쓰이는 자리
t eviction 이벤트 (메모리 부족으로 쫓겨남)
m 키 없음 (missing) 이벤트
n 새 키 (new key) 이벤트
A 모든 g$lshzxet (편의 alias)

자주 쓰는 조합

  • Ex만료 이벤트만 keyevent 채널로 — 가장 흔한 조합 (세션 만료 후속 처리)
  • Exe — manual TTL 만료 + eviction 만료 모두 받기 (메모리 압박 모니터링)
  • KEA모두 켜기 — 개발·디버깅용
  • Kg$ — 키 중심으로 generic+string 명령 — 캐시 무효화 트래커

설정 방법:

# 런타임 변경 (재시작 X)
> CONFIG SET notify-keyspace-events Ex
OK
# 현재 설정 확인
> CONFIG GET notify-keyspace-events
1) "notify-keyspace-events"
2) "Ex"

영구 적용:

# redis.conf
notify-keyspace-events "Ex"

여기서 시험 함정이 하나 있어요 — K 또는 E 둘 중 하나는 반드시 있어야 발송됨. g$lshzxe 만 박으면 발송할 채널을 안 지정한 셈이라 아무것도 안 옴. 가장 자주 보이는 "왜 이벤트 안 오나" 의 원인 2순위.

패턴 1: 세션 만료 후속 처리

가장 흔한 활용처. 사용자 세션이 만료될 때 로그아웃 처리·정리 작업·통계 기록 같은 후속 작업을 거는 자리.

설정

> CONFIG SET notify-keyspace-events Ex

Python 예제

import redis
r = redis.Redis(decode_responses=True)

pubsub = r.pubsub()
pubsub.psubscribe("__keyevent@0__:expired")

# 세션 박기 (60초 후 만료)
r.set("session:user42", "{...}", ex=60)

# 다른 워커: 만료 이벤트 받기
for msg in pubsub.listen():
    if msg["type"] == "pmessage":
        expired_key = msg["data"]    # "session:user42"
        if expired_key.startswith("session:"):
            uid = expired_key.split(":")[1]
            log_session_end(uid)     # 통계·정리 작업

psubscribe = pattern subscribe (와일드카드 지원). 단순 subscribe 는 정확한 채널 이름만 받아요.

주의 — 정확한 시각 보장 X

여기서 정말 중요한 시험 함정 — 만료 이벤트는 정확히 그 시각에 발송되지 않아요. 55편 lazy + active expiration 메커니즘 때문에 실제 만료된 후 약간 늦게 도착할 수 있어요. 접근 시 만료 발견 이면 즉시 이벤트가 나가지만, active expiration 으로 정리 되는 경우엔 정리 시점에 발송되어 수 초 지연이 생길 수 있어요.

"정확히 만료 순간에 X를 해야 한다" 가 요구사항이라면 keyspace notifications 만으로 부족해요 — 별도 스케줄러 또는 delayed queue (53편 Sorted Set 패턴) 검토.

패턴 2: 캐시 무효화 트래커

> CONFIG SET notify-keyspace-events "K$"
pubsub.psubscribe("__keyspace@0__:cache:*")

for msg in pubsub.listen():
    if msg["type"] == "pmessage":
        cmd = msg["data"]            # "set"·"del" 등
        channel = msg["channel"]
        key = channel.split(":", 2)[2]   # 키 이름 추출
        log_cache_event(cmd, key)

"누가 어느 캐시 키를 언제 갱신했나" 추적용. 캐시 운영 디버깅에 유용해요.

패턴 3: Eviction 모니터링 — 메모리 압박 감지

> CONFIG SET notify-keyspace-events Et
pubsub.psubscribe("__keyevent@0__:evicted")

evicted_count = 0
for msg in pubsub.listen():
    if msg["type"] == "pmessage":
        evicted_count += 1
        if evicted_count > 100:    # 임계 초과
            alert("Redis eviction surge!")
            evicted_count = 0

eviction 이 자주 발생 = 메모리 부족 신호. 알람 트리거로 빠르게 대응 가능.

한계 — Redis 7+ 의 개선 (Sharded Pub/Sub)

여기까지 따라오셨다면 한 가지 의문 — "Redis Cluster(분산 노드 구성) 환경에서도 keyspace notifications 가 작동하나요?". 답은 "각 노드별로 작동, 클러스터 전체 한 채널은 X".

Redis Cluster 의 전통적 Pub/Sub 은 모든 노드에 broadcast(전 노드 전파) 라 대규모에서 부담이 커요. Redis 7+ 는 Sharded Pub/Sub (SSUBSCRIBE·SPUBLISH) 으로 특정 슬롯만 메시지 받는 효율적 모델을 도입했어요.

다만 keyspace notifications 자체는 sharded 가 아닌 일반 pubsub 기반 — Cluster 환경에서는 각 노드에 개별 subscribe 가 필요해요. 68편 Cluster 에서 풀어요.

부작용·제한

Pub/Sub 의 본질적 한계 — Fire-and-Forget

54편 Stream 비교에서 본 대로, Pub/Sub 은 휘발성. 구독자가 끊겨 있는 동안 발생한 이벤트는 영원히 손실.

즉 keyspace notifications 는 항상 살아 있는 워커 가 listen 해야 하고, 워커 재시작 동안 발생한 만료 이벤트는 손실 되며, 재처리 보장 없음(at-most-once, 최대 1회 전달) 이라는 세 가지 제약을 그대로 안고 가요.

100% 보장이 필요한 후속 처리 라면 keyspace notifications 만으로 부족해요. Stream + Consumer Group (54편) 으로 상태 변화를 명시적으로 publish 하는 패턴이 더 안전.

성능 영향

이벤트 발송 자체가 비용 — 모든 명령이 발송 대상이면 상당한 CPU 사용. 운영 환경에서는 최소 flag 만 켜는 게 원칙. KEA 같은 모두 켜기 는 개발 환경만.

시험 직전 한 번 더 — Keyspace Notifications 함정 압축 노트

  • Keyspace Notifications = 키 이벤트를 Pub/Sub 으로 발송
  • 기본값 = 꺼져 있음 (notify-keyspace-events "") — "왜 안 오나" 원인 1순위
  • 두 채널 = __keyspace__ (키 중심) · __keyevent__ (이벤트 중심)
  • keyspace 채널 메시지 = 명령 이름 (set·del·expired 등)
  • keyevent 채널 메시지 = 키 이름
  • 자주 쓰는 채널 = __keyevent__ (이벤트 중심 광역)
  • 설정 flag 핵심 = K·E·g·$·l·s·h·z·x·e·t·m·n·A
  • K 또는 E 둘 중 하나는 반드시 — 안 박으면 발송 안 됨 (원인 2순위)
  • Ex = 만료 이벤트만 keyevent 채널로 — 세션 만료 후속 처리 표준
  • Exe = 만료 + eviction 모두
  • KEA = 모두 켜기 (개발용)
  • 런타임 변경 = CONFIG SET notify-keyspace-events ...
  • 영구 = redis.conf 또는 CONFIG REWRITE
  • 만료 이벤트는 정확한 시각 보장 X — lazy + active expiration 때문에 지연 가능
  • 정확한 시각 필요 = 별도 스케줄러 또는 Sorted Set delayed queue (53편)
  • Pub/Sub 기반 = fire-and-forget, 구독자 끊겨 있는 동안 손실
  • 100% 보장 필요 = Stream + Consumer Group (54편) 으로 대체
  • Cluster 환경 = 각 노드 개별 subscribe 필요 (전통 Pub/Sub 모델)
  • Redis 7+ Sharded Pub/Sub = SSUBSCRIBE·SPUBLISH (별도)
  • 성능 영향 = 모든 명령 발송 시 CPU 부담 — 최소 flag 만 켜는 게 원칙
  • 자주 쓰는 패턴 = 세션 만료 후속 처리 · 캐시 무효화 트래커 · eviction 모니터링

공식 문서: Redis Keyspace Notifications 에서 모든 flag 와 사양을 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!