← 학습 카테고리

Learn

Spark

84개 모듈 · 현재 67번째

Spark 모듈 67/84 spark-learn-67

Stream-Stream Join — 두 스트림을 실시간으로 맞춰 붙이기

Spark Structured Streaming stream-stream join 및 Delta Lake Time Travel 공식 문서 종합 (웹 리서치, 2026) — Apache Spark 공식문서(Structured Streaming Programming Guide — Join Operations), Delta Lake 공식문서(Time travel) Apache Spark 공식문서 Structured Streaming Programming Guide — Join Operations

이 모듈을 다 읽으면

  • stream-stream join에서 양쪽 스트림 모두를 상태로 버퍼링해야 하는 이유를 설명할 수 있다
  • inner join과 outer join에서 워터마크·시간 조건의 필수 여부가 왜 다른지 설명할 수 있다

spark-learn-53(RocksDB State Store)에서 상태기반 연산의 예시로만 스쳐 지나갔던 stream-stream join을 전제로, 왜 이 조인이 양쪽 스트림을 전부 상태로 버퍼링해야 하는지, 그리고 워터마크가 inner join에서는 선택이지만 outer join에서는 왜 필수가 되는지를 정리한다.

왜 양쪽 스트림을 전부 버퍼링해야 하는가

배치 조인은 양쪽 테이블이 이미 전부 존재하지만, 두 스트림을 조인할 때는 어느 한쪽의 레코드가 미래에 도착할 상대편 레코드와 언제 매칭될지 미리 알 수 없다. 그래서 Structured Streaming 엔진은 두 입력 스트림 모두에 대해 지금까지 들어온 데이터를 스트리밍 상태(streaming state)로 버퍼링해두고, 상대편에서 매칭되는 레코드가 도착하는 즉시 조인 결과를 만들어낸다 — 클릭 스트림과 노출(impression) 스트림을 예로 들면, 두 이벤트가 각각 관련된 채로 수신되는 순간 조인 결과가 나온다. 문제는 워터마크나 시간 범위 조건을 아예 지정하지 않으면 Spark가 각 스트림의 상태를 무기한 버퍼링한다는 것이다 — 오래된 데이터도 언젠가 매칭될 수 있다는 가능성을 배제할 수 없기 때문이다.

핵심 포인트

  • 양쪽 스트림 모두를 상태로 버퍼링해 상대편의 미래 도착 레코드와 매칭 — 배치 조인과 근본적으로 다른 지점
  • 워터마크·시간 범위 조건이 없으면 상태를 무기한 버퍼링(오래된 데이터도 매칭 가능성이 있다고 봄) — 상태가 무한정 커지는 위험

inner join은 선택, outer join은 필수 — 왜 다른가

inner join은 어떤 컬럼·조건으로든 자유롭게 걸 수 있고, 워터마크와 이벤트 시간 제약(join interval)은 선택 사항이다 — 물론 없으면 위에서 본 무기한 버퍼링 문제가 생기므로 실무에서는 권장되지만, 조인 자체는 이 조건 없이도 동작한다. 반면 outer join(left/right)에서는 워터마크와 이벤트 시간 제약을 반드시 지정해야 한다 — outer join이 NULL 결과를 만들어내려면(예: 왼쪽 레코드가 끝내 오른쪽과 매칭되지 않았다는 결론) '이 레코드는 앞으로도 절대 매칭되지 않을 것'이라는 확신이 필요한데, 그 확신은 오직 워터마크가 지나갔다는 사실로만 얻을 수 있기 때문이다. 워터마크 없이는 엔진이 '아직 안 온 것뿐'인지 '영원히 안 올 것'인지 구분할 방법이 없다.

핵심 포인트

  • inner join: 워터마크·시간 제약 선택사항(없어도 동작은 하나 상태 무한 증가 위험)
  • outer join: 워터마크·시간 제약 필수 — NULL 결과를 내려면 '앞으로도 절대 매칭 안 됨'을 확신할 방법이 워터마크뿐이기 때문

마이크로배치 엔진에서의 실제 동작 — 워터마크 갱신 지연

마이크로배치 엔진에서는 워터마크가 매 마이크로배치가 끝날 때 갱신되고, 그다음 마이크로배치가 그 갱신된 워터마크를 이용해 상태를 정리하고 outer join의 NULL 결과를 출력한다. 이 말은 outer join의 NULL 결과가 나오는 시점이 '워터마크를 넘겼다고 판단된 그다음 배치'로 밀린다는 뜻이다 — 만약 그 사이 새 데이터가 전혀 들어오지 않으면(스트림이 조용해지면) 워터마크 자체가 갱신되지 않아 outer 결과 생성도 함께 지연될 수 있다. spark-learn-44에서 다룬 워터마크의 '최대 관측 이벤트시간 기준 이동'이라는 원리가 여기서도 그대로 적용되는 것이다.

핵심 포인트

  • 워터마크는 마이크로배치가 끝날 때 갱신되고, 그다음 배치가 이를 이용해 상태 정리+outer NULL 결과 출력
  • 새 데이터가 안 들어오면 워터마크도 안 갱신돼 outer join의 NULL 결과 생성 자체가 지연될 수 있음