← Apache Flink 코스

Courses · Day 2

핵심개념 A — 데이터 흐름을 다루는 법

스텝 5 / 17

5 / 17
개념 flink-course-05

keyBy — 논리적 파티셔닝과 상태의 연결

직관적으로

우체국에서 우편물을 지역 코드별로 나눠 각 지역 담당자에게 보내는 걸 생각해보자. keyBy는 정확히 이런 역할이다 — 스트림의 각 레코드를 특정 '키(지역 코드)' 기준으로 나눠, 같은 키를 가진 레코드는 항상 같은 담당자(TaskManager의 특정 슬롯)에게로 보낸다.

실제 사례

`stream.keyBy(x -> x.userId)`라고 하면, user_id가 "alice"인 레코드는 항상 특정 슬롯으로, "bob"인 레코드는 항상 다른(혹은 같은) 특정 슬롯으로 라우팅된다 — 이게 Kafka의 파티션 키 개념과 가장 비슷하다. Spark의 `groupBy`는 '그 순간 모여 있는 데이터를 한 번에 묶어서' 처리하는 배치적 개념인 반면, keyBy는 '이후의 모든 연산이 이 키 단위로 계속 독립적으로 유지되게' 만드는 스트리밍 고유의 개념이다.

정확한 원리

keyBy는 논리적 파티션(logical partition)을 만든다 — 이후에 오는 모든 상태 기반 연산(윈도우 집계, Keyed State 등)은 이 키 단위로 완전히 독립적으로 실행되고 저장된다. 내부적으로 Flink는 키의 해시값을 기준으로 어느 서브태스크가 그 키를 담당할지 결정하며(키 그룹, key group), 같은 키는 항상 같은 서브태스크로 라우팅된다는 게 보장된다 — 이 보장이 있어야 나중에 배울 Keyed State가 안전하게 동작할 수 있다.

흔한 오해

"keyBy는 그냥 Spark의 groupBy를 스트리밍 버전으로 이름만 바꾼 것"이라는 오해가 흔하다. 정확히는 groupBy가 '지금까지 모인 데이터를 한 번 그룹핑해서 집계 결과를 낸다(끝나는 연산)'인 반면, keyBy는 '이후 스트림 전체가 이 키 기준으로 영구히 파티셔닝된 상태로 계속 흘러간다(끝나지 않는 라우팅)'는 근본적 차이가 있다.

확인 문제

`keyBy("user_id")` 이후에 카운트를 세는 연산을 붙였다고 하자. 이 카운트는 "전체 스트림에 대한 카운트"일까, 아니면 "user_id별로 각각 독립된 카운트"일까? 왜 그런가?