알람 서비스에서 Redis의 PUB/SUB 기능을 사용하여 유저에게 실시간 알람 기능을 구현하였다.
유저 동접수가 증가함에 따라 개발에서 유저별 Redis 커넥션을 생성하였고 Elasticache Redis의 최대 커넥션 수 인 65,000에 가깝게 사용하여 스케일아웃을 진행하여 이슈 대응을 진행하였는데 PUB/SUB 기능과 해당 사용 목적에 대하여 미리 확인하고 이슈 대응 및 구조 개선을 검토 진행.
(구조 예시)

REDIS PUB/SUB 기능을 사용하여 실시간 유저 알람을 제공하고 있습니다.
현재 구조는 클라이언트(유저별) 채널(토픽)이 생성되어 있습니다
따라서 유저가 증가함에 따라 레디스의 커넥션과 채널이 추가되는 구조입니다.
REDIS PUB/SUB의 커넥션 풀 사용시 GET/SET 같은 일반적인 명령어와는 달리 Sub 요청이 발생했을 때 요청 즉시 커넥션을 만들고 커넥션 풀 안에 저장한 후 유저가 연결을 종료하면 커넥션 풀에서 제거하여 Close 하는 방식으로 처리가 됩니다.
즉 Poolsize, Pooltimeout, min max idleconns 등등이 동작하지 않음.
현재 구성
- 유저 요청마다 토픽을 만들고, 그 토픽에 대해 REDIS 구독 요청.
- 장점
- 서버는 자신이 실제로 처리할 계정만 담당하므로, 불필요한 메시지 수신 최소화 → 서버 I/O, CPU 효율적
- 메모리/네트워크 사용 최적화 → 연결되지 않은 유저의 메시지는 수신하지 않음
- 단점
- Redis 연결 수가 많아질 수 있음 → 서버에서 커넥션 관리 필요
- 장점
- REDIS PUB/SUB의 Topic이 다수 생성됨 (접속한 유저마다 토픽 생성)
- 접속하지 않은 유저는 토픽 생성 및 작업이 없으므로 이점이 있음
- 커넥션이 다수 생성되는 단점이 있음
커넥션 수를 줄이기 위해서는 중간의 SSE 통신을 하는 Pod에서 유저를 관리하는 방안이 있습니다.
- 서비스 시작시 모든 토픽을 구독하고, 메시지 수신하면, 토픽에 맞는 클라이언트 커넥션에 메시지를 분배
- 장점
- 요청 수가 많아져도 레디스 커넥션은 늘어나지 않음. 레디스 커넥션은 통합 알림 서버 대수에 의존
- 단점
- 불필요한 메시지 수신 발생 → 연결되지 않은 유저 메시지까지 수신 → 네트워크, CPU 낭비
- 장점
- 토픽을 기능마다 생성하고 관리함
- 토픽은 구독은 API 서버 기준으로만 할당됨
- API 서버에서 모든 유저에 대하여 작업 처리가됨
아래는 PUB/SUB 기능을 바탕으로 GPT와 사용 방식에 대하여 확인한 내용입니다.
일반적으로 사용자는 Redis에 직접 연결하지 않고, 각 Pod가 Redis와 구독 연결을 맺고 사용자와는 SSE/WebSocket으로 통신합니다.
1. 사용자별 채널(토픽) 1:1 라우팅
user:{uid} 같은 채널에만 퍼블리시. 각 Pod는 “자기 Pod에 붙어 있는 사용자들”의 채널만 구독.
장점
- 네트워크/CPU 낭비 최소화: 타깃에게만 전달→ Redis→Pod 간 불필요한 트래픽이 거의 없음.
- 격리와 보호: 특정 유저/그룹 폭주가 다른 유저들에 미치는 영향이 작음(핫 유저만 해당 Pod/채널이 바쁨).
- 권한/프라이버시 단순화: 채널 이름 수준에서 자연스러운 경계(잘못된 수신 위험↓).
- 프레즌스(presence) 판단 용이: 유저 연결 해제 시 해당 채널 구독이 사라져 “온라인 여부” 캐싱/추론이 쉬움.
- 세밀한 레이트 리밋: 사용자/채널 단위로 손쉽게 속도 제한·드랍 정책을 두기 좋음.
단점
- 구독 churn 비용: 접속/이탈이 잦으면 SUBSCRIBE/UNSUBSCRIBE가 빈번해져 Redis와 애플리케이션 양쪽 모두 부하 증가.
- 채널/구독 메타데이터 급증: 수십/수백만 사용자 동시 접속 시 Redis의 구독 테이블·Pod의 라우팅 테이블 메모리/관리 비용이 커짐.
- 운영 난이도↑: 모니터링/디버깅(“누가 무엇을 구독했는가”)과 롤링 배포 시 재구독 스톰 등 운영 이슈.
- 클러스터 환경에서의 브로드캐스트 비용 이슈: (Redis 7 미만 또는 글로벌 Pub/Sub만 쓸 때) 채널 수가 많아지면 클러스터 전파 비용/라우팅 복잡성이 커짐. → Sharded Pub/Sub(SSUBSCRIBE) 사용을 권장.
2. 통합(소수) 채널 + 서버측 필터링
notifications, chat-events 같은 소수 채널에 모든 이벤트를 퍼블리시. 각 Pod가 공통 채널을 구독한 뒤, 메시지 내 타깃(유저/룸/테넌트)을 보고 Pod에서 필터링/팬아웃.
장점
- 단순한 구독 구조: 채널·구독 수가 Pod 수 수준이라 설정·모니터링·배포가 쉽다.
- 구독 churn 거의 없음: 유저 접속/이탈과 무관하게 Pod의 구독은 고정.
- Redis 메모리/관리 부담↓: 구독 메타데이터가 작다.
- 메시지 스키마 진화 용이: 이벤트 형식이 변해도 구독 변경 없이 Pod 코드만 배포하면 됨.
단점
- 불필요한 전달/필터 비용: 모든 메시지가 모든 Pod로 가서 N(Pod) 배의 네트워크/디코딩/필터링 오버헤드.
- “핫 채널” 병목: 통합 채널이 고QPS이면 Redis egress와 각 Pod의 소비 속도에 민감. 느린 Pod는 Pub/Sub 출력 버퍼가 차서 끊길 위험.
- 멀티 테넌시/격리 취약: 한 테넌트의 트래픽이 전체에 영향(스파이크 전파).
- 보안/권한은 애플리케이션 책임: 올바른 필터링/권한 체크가 코드 버그 없이 항상 이뤄져야 한다.
3. Pod‑Inbox + Sharded Pub/Sub
불필요한 브로드캐스트를 줄이면서 Redis 구독 수를 O(Pods)로 고정
- Presence(접속 위치) 저장
- 사용자 연결이 열릴 때 connID를 발급하고, “이 연결이 어느 Pod에 붙어있는지”를 Redis에 기록합니다.
- 예)
- SETEX conn:{connID} {podID} 120s (TTL은 주기적으로 갱신)
- HSET presence:user:{userID} {connID} {podID} (사용자→연결 매핑)
- SADD presence:pod:{podID} {connID} (Pod가 가진 연결 집합)
- 각 Pod는 자기 inbox 채널만 SSUBSCRIBE
- 채널명: pod:{<podID>} 처럼 해시태그({}) 안에 샤드 키를 넣어 특정 슬롯에 고정합니다.
- 각 Pod는 SSUBSCRIBE pod:{<podID>} 1개만 유지합니다.
- Publish 경로(단일/소수 타깃)
- 발행자가 대상 사용자들의 현재 Pod를 presence에서 조회 → podID -> [connIDs]로 묶기
- 각 podID에만 SPUBLISH pod:{podID}로 메시지를 보냅니다(최소 전파).
- Pod는 수신 메시지의 connIDs를 보고 자신의 SSE/WebSocket 연결로만 팬아웃.
- Broadcast/대규모 그룹
- 전역 1개가 아니라 주제/테넌트/대형 룸 단위로 몇십~몇백 개의 Sharded 토픽(예: topic:{tenantA}, room:{123})을 운용하고 해당 토픽을 SSUBSCRIBE.
- 진짜 전원 방송이 필요할 때만 소수의 통합 채널을 사용.
- 내구성이 필요하면
- Pub/Sub은 at‑most‑once입니다. 오프라인 보관/재전송이 필요하면 Redis Streams로 저장하고, 온라인 경로는 Pub/Sub로 저지연 팬아웃.
'공부 > DATABASE' 카테고리의 다른 글
| [Aurora, Elasticache] 대역폭 관련 (0) | 2025.10.08 |
|---|---|
| [Aurora PostgreSQL] Toast 테이블과 VACUUM (0) | 2025.10.08 |
| [Aurora PostgreSQL] 파티션 테이블 전환 (0) | 2025.10.08 |
| [AWS] DocumentDB Garbage Collection (0) | 2025.07.05 |
| [PostgreSQL] VACUUM 설정 적용 케이스 (5) | 2025.07.05 |