Apache Pulsar와 Redis Streams로 설계하는 실시간 분석 파이프라인
Event-Driven Architecture를 바탕으로 Apache Pulsar와 Redis Streams를 조합해 실시간 분석 파이프라인의 신뢰성, 확장성, 운영 방식을 설계한다.
2026-08-14 · 최초 발행 2025-10-14
이벤트 로그와 즉시 처리 경로를 함께 설계할 때
실시간 분석 파이프라인은 이벤트를 오래 보관하고 다시 재생할 수 있어야 하며, 동시에 지연이 민감한 처리도 감당해야 한다. Event-Driven Architecture(EDA)는 생산자와 소비자를 비동기 이벤트로 느슨하게 연결하고, 이벤트 로그를 단일 진실 소스로 사용한다. 재생과 백필(Backfill)을 이용한 회복 탄력성도 이 구조에서 확보한다.
Apache Pulsar와 Redis Streams는 같은 문제를 다른 방식으로 다룬다. Pulsar는 Broker와 BookKeeper를 분리한 컴퓨트·스토리지 구조를 기반으로 다중 테넌시, 토픽·서브스크립션, 트랜잭션, 메시지 중복 제거, 티어드 스토리지를 제공한다. Redis Streams는 인메모리 로그 스트림과 소비자 그룹, PEL(Pending Entries List)을 통해 최소 한 번(at-least-once) 처리를 지원하며, 초저지연 경량 처리와 단순한 운영에 강점이 있다.
배치 중심 구조를 이벤트 중심으로 옮기면 비용, 지연, 복잡도를 함께 줄이는 방향으로 파이프라인을 구성할 수 있다.
입력부터 서빙까지 나누는 데이터 경로
입력 계층에는 API/SDK, CDC(Change Data Capture), IoT 게이트웨이, 로그 셰퍼가 들어간다. 이 단계에서는 스키마 레지스트리와 이벤트 버전 전략을 함께 연결한다.
처리 계층은 Flink·Spark 같은 스트림 처리 엔진, Pulsar Functions, Redis Lua·모듈 기반의 경량 처리로 구성할 수 있다. 상태 저장과 타임윈도 집계도 이 구간에서 다룬다. 결과는 Elastic·ClickHouse 같은 서빙 DB, 알림·워크플로 같은 피드백 채널, Object Storage 기반 장기 보관소로 분리한다.
입력·처리·출력 단계를 분리하면 실패 시 DLQ와 PEL로 빠지는 경로, 그리고 재처리 루프를 명시할 수 있다. Pulsar에서는 트랜잭션으로 Publish와 Ack의 원자성을 확보하고, Redis에서는 PEL과 멱등키로 일관성을 보완한다.
신뢰성과 확장성의 책임을 나누는 법
Pulsar는 파티션 복제, 메시지 중복 제거, 생산과 소비를 묶는 트랜잭션을 지원한다. 정확히 한 번(Exactly-once) 달성 여부는 엔진·버전·커넥터에 따라 달라지므로 최신 정보를 확인해야 한다. 스토리지인 BookKeeper와 컴퓨트인 Broker를 독립적으로 확장할 수 있고, 티어드 스토리지로 장기 이벤트 저장 비용을 줄일 수 있다.
Redis Streams에서는 소비자 그룹과 PEL이 재처리 기반이 된다. XACK와 XAUTOCLAIM으로 장애 이후 처리를 복구하며, 정확히 한 번은 애플리케이션 레벨의 멱등성 설계로 구현한다. Redis Cluster 샤딩으로 수평 확장은 가능하지만, 키 설계와 메모리 관리 정책이 성능과 운영 안정성을 좌우한다.
모니터링은 Prometheus/Grafana, Pulsar Manager, Redis INFO 지표를 기준으로 구성한다. 백프레셔와 리소스 쿼터링을 적용하고, TLS, Tokens·ACL 기반 인증·인가, 네임스페이스·테넌시 분리, 전송·저장 데이터 암호화 정책을 함께 설계한다.
요구사항에서 운영 지표까지 이어지는 설계
먼저 지연(SLA), 처리량(TPS/MBps), 보존 기간, 정확성 수준(At-least/Exactly-once)을 정한다. 멱등 키와 재처리 전략, 스키마 진화 정책의 호환성 모드(backward/forward)도 이 단계에서 결정한다.
이벤트 유형과 파티션 키는 유저ID, 디바이스ID, 샤드 키를 기준으로 모델링한다. 핫파티션을 줄이기 위해 해싱과 키 로테이션을 고려한다.
장기 보관, 다테넌시, 크로스-리전 요구가 중심이면 Pulsar를 우선한다. 초저지연 경량 처리, 간단한 운영, 캐시 근접성이 더 중요하다면 Redis Streams가 맞다. 혼합 구조에서는 코어 이벤트 로그를 Pulsar에 두고, 초저지연 세션 집계는 Redis Streams로 분리한다.
Pulsar에서는 트랜잭션, DeadLetterPolicy, 재시도 백오프, 메시지 중복 제거를 활성화한다. Redis에서는 소비자 그룹, PEL 스캔 주기, XAUTOCLAIM 임계치, 키 기반 UPSERT 멱등성을 준비한다. Pulsar의 Broker·Bookie·ZooKeeper(or etcd)는 자원을 분리하고 티어드 스토리지와 토픽·서브스크립션 네임스페이스 표준화를 구성한다. Redis는 Cluster 슬롯 균형, maxmemory 정책(noeviction 지양), RDB/AOF 스냅샷과 복제를 관리한다.
SLO에는 레이턴시 p95/p99, 컨슈머 랙, 재처리율, DLQ·PEL 증가 추이를 포함하고 자동 스케일링 트리거 규칙을 둔다.
스트림 플랫폼의 선택 기준
| 지표 | Apache Pulsar | Redis Streams |
|---|---|---|
| 성능(지연) | 밀리초대, 디스크/네트워크 영향 존재, 일관된 지연 제공 | 서브-밀리초~수 ms, 인메모리 기반 초저지연 |
| 확장성 | 컴퓨트/스토리지 분리 확장, 티어드 스토리지 지원 | Cluster 샤딩 기반 수평 확장, 메모리 용량이 한계 |
| 일관성 | 트랜잭션/중복 제거/다중 구독 모델, 재생 용이 | At-least-once, PEL/XAUTOCLAIM, 멱등성으로 보완 |
| 안정성 | 북키퍼 저널링, 내장 DLQ/백오프, 장기 보관 안정 | 단일 노드 장애 시 레플리카/센티널로 복구, PEL 복원 |
| 운영 편의 | 다테넌시/네임스페이스 관리, 커넥터/함수 생태계 | 간단한 운영, 낮은 학습곡선, 키/메모리 관리 중요 |
실제 수치는 하드웨어, 네트워크, 워크로드에 의존하므로 벤치마크를 먼저 수행한다.
Pulsar 생산자와 소비자
전제: Python 3.10+, pulsar-client>=3.x, Pulsar 2.10+ (트랜잭션 사용 시 브로커 설정 필요, 최신 정보 확인 필요)
# pip install pulsar-client==3.4.0
import pulsar, json, time
service_url = "pulsar://localhost:6650"
topic = "persistent://analytics/ns/events"
client = pulsar.Client(service_url)
producer = client.create_producer(topic, batching_enabled=True)
# Producer
producer.send(json.dumps({"event":"click","user_id":"u1","ts":time.time()}).encode("utf-8"))
# Consumer with retry/DLQ via negative ack
consumer = client.subscribe(topic, subscription_name="agg-sub", consumer_type=pulsar.ConsumerType.Shared)
try:
while True:
msg = consumer.receive(timeout_millis=5000)
payload = json.loads(msg.data())
# 멱등 처리 로직 추가 (예: upsert by event_id)
if payload.get("event") == "bad":
consumer.negative_acknowledge(msg) # 재시도 -> DLQ 정책 활성화 시 DLQ 이동
else:
consumer.acknowledge(msg)
except pulsar.Timeout:
pass
finally:
consumer.close()
producer.close()
client.close()
토픽 파티션 수는 TPS와 메시지 사이즈를 기준으로 사전에 산정한다. 메시지 압축(zstd), 배칭 최적화, DeadLetterPolicy, backoffRetry, 스키마 레지스트리도 함께 적용한다.
Redis Streams 소비자 그룹
전제: Redis 7.x+, redis-py>=5.x, Stream과 Group 사전 생성 또는 자동 생성 처리
# pip install redis==5.0.6
import json, time
from redis import Redis
r = Redis(host="localhost", port=6379, decode_responses=True)
stream, group, consumer = "events", "agg", "c1"
try:
r.xgroup_create(name=stream, groupname=group, id="$", mkstream=True)
except Exception:
pass # 이미 존재
while True:
resp = r.xreadgroup(groupname=group, consumername=consumer, streams={stream: ">"}, count=100, block=2000)
if not resp:
continue
for _, messages in resp:
for msg_id, fields in messages:
try:
event = json.loads(fields["data"])
# 멱등 처리 (event_id 키 기반 upsert)
# 처리 성공
r.xack(stream, group, msg_id)
except Exception:
# 실패 시 ack 생략 -> PEL에 남음
pass
# 오래된 PEL 자동 회수
r.xautoclaim(stream, group, consumer, min_idle_time=60000, start="0-0", count=100)
Stream 키 샤딩 전략은 events:{user_shard}처럼 수립하고, maxmemory 정책과 TTL로 보관 기간을 제어한다. 장애로 고아 메시지가 생기면 XAUTOCLAIM으로 회수하며 XINFO와 consumer lag를 추적한다.
파이프라인이 필요한 업무 흐름
클릭스트림에서는 Pulsar로 이벤트를 장기 보관·재생하고, Flink 집계 결과를 Pinot로 서빙할 수 있다. 초저지연 세션 카운팅은 Redis Streams 캐시로 가속한다.
결제 이상징후 탐지에서는 Redis Streams로 10~100ms 내 피드백 루프를 구성하고, 규칙·피쳐 캐시 근접 처리를 수행한다. 확정 이벤트는 Pulsar에 영속화해 감사 추적에 활용한다.
IoT 텔레메트리에서는 Pulsar의 멀티테넌시로 디바이스를 격리하고, Functions로 라우팅과 필터링을 수행하며, 티어드 스토리지에 장기 원본을 보관한다. 경량 임계치 알림은 Redis 측 실시간 윈도우 계산으로 처리한다.
운영 가시성에서는 애플리케이션 로그·메트릭 이벤트를 Pulsar로 수집하고, 이상 케이스는 DLQ로 분리한다. Redis는 슬라이딩 윈도우 기반 알림 임계치 계산에 사용할 수 있다.
지연과 보관 비용에 미치는 변화
알림·지표 지연은 p95 기준 분 단위에서 초 단위로 전환되고, 컨슈머 랙은 80% 이상 감소한다. 티어드 스토리지와 압축은 장기 보관 비용을 30%+ 절감한다(환경 의존).
이벤트 재생은 회복 탄력성을 높이며, 배치와 실시간으로 나뉜 이중 파이프라인을 제거해 운영을 단순화한다. 도메인 이벤트를 중심으로 설계하면 서비스 간 결합도가 낮아지고 기능 출시 리드타임도 단축된다.
운영에서 확인할 경계 조건
스키마 진화 규칙은 backward 호환으로 고정하고, 멱등키를 표준화하며, DLQ·PEL 재처리 워크플로를 자동화한다. 지연 p95/p99, 컨슈머 랙, 재시도율, 실패율, 저장소 사용량을 SLO로 관리한다. TLS, 토큰 기반 인증, 네임스페이스 격리, 최소 권한 원칙도 기본 운영 항목이다.
Pulsar는 유연성과 영속성을 얻는 대신 운영 복잡도가 커지고 ZooKeeper·BookKeeper 운영 부담이 따른다. Redis Streams는 간결성과 초저지연을 제공하지만 메모리 비용과 데이터 보존에 제약이 있으며, 정확히 한 번语 의미적 보장은 애플리케이션의 책임이다.
코어 이벤트 버스에는 Pulsar를, 캐시 근접 집계와 초저지연 경량 처리에는 Redis Streams를 배치하면 각 플랫폼의 성격을 분리할 수 있다. 파티션, 샤딩, 보존 정책은 단계적으로 도입하고 벤치마크를 바탕으로 조정한다.