foreach()/ForeachWriter — 마이크로배치가 아니라 레코드 하나씩 쓴다
PySpark mapInPandas 및 Structured Streaming foreach/ForeachWriter 공식 문서 종합 (웹 리서치, 2026) — Apache Spark 공식문서(pyspark.sql.DataFrame.mapInPandas, pyspark.sql.GroupedData.applyInPandas), Databricks 공식 문서(structured-streaming/foreach) Databricks 공식 문서 Use foreachBatch to write to arbitrary data sinks
이 모듈을 다 읽으면
- foreach()가 spark-learn-59의 foreachBatch와 처리 단위(레코드 vs 배치)에서 어떻게 다른지 설명할 수 있다
- spark-learn-57의 Continuous Processing에서 왜 foreachBatch 대신 foreach()를 써야 하는지 설명할 수 있다
spark-learn-59에서 다룬 foreachBatch(마이크로배치 출력을 배치 DataFrame으로 노출)와 대비해, 레코드 하나하나에 대해 직접 쓰기 로직을 실행하는 foreach()의 동작과, 이게 필요해지는 상황을 정리한다.
foreachBatch와의 처리 단위 차이 — 배치 전체 vs 레코드 하나
spark-learn-59의 foreachBatch는 마이크로배치 하나의 출력 전체를 평범한 배치 DataFrame으로 넘겨줘, 그 안에서 배치 전용 커넥터나 연산을 자유롭게 쓸 수 있게 했다. foreach()는 이와 달리 각 행(레코드) 하나하나에 대해 개발자가 정의한 쓰기 로직을 실행한다 — ForeachWriter를 구현하려면 open(연결 초기화)/process(레코드 하나 처리)/close(연결 정리) 세 메서드를 구현해야 하고, 연결 관리·에러 처리·재시도·성능까지 전부 개발자 책임이 된다.
핵심 포인트
- foreachBatch(spark-learn-59): 마이크로배치 전체를 배치 DataFrame으로 노출 / foreach(): 레코드 하나하나에 대해 쓰기 로직 실행
- ForeachWriter 구현: open(연결 초기화)/process(레코드 처리)/close(연결 정리) 3개 메서드 — 연결관리·에러처리·재시도·성능까지 개발자 책임
언제 foreach()를 쓰는가 — foreachBatch를 못 쓰는 상황
실무에서는 foreachBatch가 배치 DataFrame을 그대로 다룰 수 있어 더 편리하고 일반적으로 권장된다 — 특히 DB 쓰기처럼 배치 단위가 자연스러운 경우다. 하지만 foreachBatch가 아예 선택지가 안 되는 두 경우가 있다 — (1) 대응하는 배치 데이터 라이터가 없는 싱크를 써야 할 때, (2) spark-learn-57에서 다룬 Continuous Processing 모드일 때다. foreachBatch는 근본적으로 마이크로배치 실행에 의존하기 때문에, 마이크로배치 개념 자체가 없는 Continuous Processing에서는 동작하지 않는다 — 이 경우 레코드 단위로 직접 처리하는 foreach()가 유일한 선택지가 된다.
핵심 포인트
- 일반적으로는 foreachBatch가 더 편리(배치 DataFrame 그대로 활용) — DB 쓰기 같은 배치 자연스러운 경우 권장
- foreachBatch를 못 쓰는 경우: (1) 대응하는 배치 데이터 라이터가 없는 싱크, (2) spark-learn-57의 Continuous Processing — 마이크로배치 개념이 없어 foreachBatch가 원천적으로 동작 안 함, 이때 foreach()가 유일한 선택지