스트림 처리
- 11.1 이벤트 스트림 전송
- 11.2 데이터베이스와 스트림
- 11.3 스트림 처리
현실의 데이터는 끊임없이 들어와. 일괄 처리가 유한한 데이터셋을 모아서 한꺼번에 처리하는 거라면, 스트림 처리는 이 무한한 데이터를 이벤트가 발생할 때마다 바로 처리하는 거야. 그리고 이 둘의 경계를 허물어준 게 로그 기반 메시지 브로커지.
전통적인 메시지 브로커(RabbitMQ, SQS)는 메시지를 소비자에게 전달하면 삭제해. 과거 메시지를 다시 읽을 수 없고, 일괄 처리의 입력과는 근본적으로 달라. Kafka 같은 로그 기반 브로커는 이걸 뒤집었어. 메시지를 추가 전용 로그에 쓰고, 소비자마다 오프셋으로 어디까지 읽었는지 추적하지. 삭제를 안 하니까 언제든 다시 읽을 수 있고, 소비자가 느려도 다른 소비자에 영향이 없어. 일괄 처리의 반복 가능성을 스트림의 세계에 가져온 거야.
DB와 스트림은 사실 깊이 연결돼 있어 — 복제 로그 자체가 스트림이잖아. 변경 데이터 캡처(CDC) 는 DB의 변경을 관찰해서 검색 색인, 캐시, 데이터 웨어하우스에 거의 실시간으로 반영하는 거야. 이벤트 소싱은 비슷하지만 철학이 달라 — CDC가 행 삽입/삭제 같은 낮은 수준의 변경을 캡처한다면, 이벤트 소싱은 "학생이 강좌를 취소했다" 같은 애플리케이션의 의도를 불변 이벤트로 기록하지. 감사 추적이 자연스럽게 생기고, 새 뷰가 필요하면 이벤트를 새 방식으로 재생하면 돼. DB의 현재 상태는 이벤트 로그를 시간에 대해 적분한 결과라는 수학적 이중성이 여기서 드러나.
스트림 처리의 핵심 난제는 시간과 조인이야. 이벤트 시간과 처리 시간이 다를 수 있고, 늦게 도착하는 이벤트를 어떻게 처리할지 결정해야 해. 스트림 조인은 세 가지 — 두 스트림을 시간 윈도 내에서 매칭하는 스트림-스트림 조인, 스트림 이벤트에 DB 정보를 붙이는 스트림-테이블 조인, 두 CDC 스트림으로 구체화된 뷰를 유지하는 테이블-테이블 조인. 내결함성도 일괄 처리와 달라 — 입력이 무한이니까 처음부터 재실행은 안 되고, 마이크로일괄, 체크포인팅, 트랜잭션적 접근 같은 방법을 써야 하지.
정리
11장 읽고 기억할 거:
- 로그 기반 메시지 브로커(Kafka)가 스트림 처리의 기반이다. 메시지를 삭제 안 하고 추가만 하니까 재처리 가능하고, 일괄 처리의 반복 가능성을 스트림에 가져온다
- CDC와 이벤트 소싱 — DB 변경을 스트림으로. CDC는 낮은 수준에서 추출하고, 이벤트 소싱은 의도를 기록한다. 둘 다 불변 이벤트 로그에서 파생 데이터를 만드는 패턴이다
- 스트림 조인과 시간 윈도가 핵심 난제. 이벤트 시간과 처리 시간의 차이, 늦게 도착하는 이벤트, 상태 유지 조인 — 일괄 처리에는 없던 새로운 도전이다