Topic, Partition, Record
Last updated
Last updated
카프카에서 가장 중요한 개념이다.
토픽이란 데이터를 구분하기 위해 사용하는 단위로, 1개 이상의 파티션을 소유한다.
Producer가 보낸 데이터들이 저장되며, 각각의 데이터를 레코드라고 부른다.
토픽의 데이터인 레코드를 병렬로 처리할 수 있도록 하기 위해 데이터를 파티션 단위로 나눈다.
파티션은 큐와 비슷한 FIFO 구조이며 파티션의 데이터는 컨슈머가 소비하게 된다.
파티션의 개수는 카프카의 성능과 관련이 있다. 데이터의 처리량, 메시지 키 사용 여부, 브로커/컨슈머 영향도 등을 고려해 파티션 개수를 설정해야 한다.
데이터의 처리 속도를 올리려면 컨슈머의 처리량을 늘리거나 컨슈머를 추가해 병렬 처리량을 늘려야 한다.
컨슈머의 처리량을 늘리려면 서버 사양을 올리거나 GC 튜닝을 해야 한다. 하지만 컨슈머 특성 상 외부 시스템과 연동되는 경우가 많으므로 일정 수준 이상의 처리량을 넘기기는 거의 어렵다.
컨슈머를 늘려 병렬 처리량을 늘리려면 파티션을 늘리고 컨슈머를 늘려야 한다.
파티션 개수 공식은 다음과 같다. 예를 들어 초당 1000개의 레코드를 프로듀서에서 보내고 있고 컨슈머가 초당 100개의 데이터를 처리하고 있다면, 파티션 개수는 10개 이상이 되어야 한다.
(프로듀서 전송 데이터 량) < (컨슈머 데이터 처리량) * (파티션 개수)
컨슈머 데이터 처리량을 구하기 위해서는 카프카에 더미 데이터를 넣어 테스트해보아야 한다. 이 때 로컬이나 테스트 환경에서 진행하는 것보다는 상용 환경에서 진행하는 것이 더 정확할 것이다.
메시지 키를 기준으로 파티셔닝하는 전략을 사용할 경우 파티션 개수가 달라지면 해싱이 달라져 매핑되는 파티션도 달라지게 될 것이다. 만약 메시지 키를 사용하면서 컨슈머에서 처리하는 메시지의 순서가 보장되어야 한다면, 커스텀 파티셔너를 구현하거나 처음부터 파티션 개수를 넉넉하게 지정해야 한다.
파티션은 브로커의 파일 시스템을 사용하므로 파티션이 늘어나면 파일 개수도 늘어난다. 하지만 운영 체제에서는 프로세스마다 열 수 있는 파일의 최대 개수를 제한하므로 각 브로커 당 파티션 개수를 확인하고, 만약 너무 많다면 브로커를 늘려야 한다.
delete policy
명시적으로 토픽의 데이터를 삭제한다.
데이터는 세그먼트 단위로 삭제된다. 세그먼트는 파티션마다 별개로 생성되어 오프셋 중 가장 작은 값이 세그먼트의 파일 이름이 된다.
세그먼트는 여러 청크로 나뉘며, segment.bytes 옵션으로 세그먼트의 크기를 정할 수 있다.
데이터를 저장하기 위해 사용중인 세그먼트는 액티브 세그먼트라고 한다.
토픽의 데이터 삭제 기준은 시간 혹은 용량이 될 수 있다.
카프카는 일정 주기마다 세그먼트 파일의 마지막 수정 시간이 retention.ms 속성값을 넘어가거나 세그먼트 파일의 크기가 retention.bytes를 넘어갔다면 해당 세그먼트 파일을 삭제한다.
compact policy
여기서 의미하는 압축이란 메시지 키 마다 가지는 레코드 중 오래된 데이터를 삭제하는 것이다.
메시지 키를 기준으로 제거하기 때문에 하나의 파티션에서 오프셋의 일부가 삭제되어 오프셋의 증가가 일정하지 않을 수 있다.
스트림즈의 KTable과 같이 메시지 키를 기반으로 데이터를 처리할 때 유용하다. 메시지 키에 해당하는 가장 최신의 데이터만 유효한 경우 나머지 데이터는 제거하는 것이 좋기 때문이다.
아래 그림에서 Ale, Ben 이라는 메시지 키에 대한 새로운 레코드가 생성되면 기존 레코드는 제거된다. Tim이라는 메시지 키에 대한 값이 null인 레코드가 들어오면 해당 메시지 키에 대한 모든 레코드를 제거하겠다는 의미를 나타냄도 확인할 수 있다.
min.cleanable.dirty.ratio 속성값은 액티브 세그먼트를 제외한 세그먼트들에 남아 있는 데이터의 테일 영역 레코드 개수와 헤드 영역 레코드 개수의 비율을 지정한다. 정확히는 (헤드 영역 메시지 개수) / (헤드 영역 메시지 개수 + 테일 영역 메시지 개수) 를 의미한다.
테일 영역은 압축 정책에 의해 압축이 완료된 레코드를 의미한다. 이 영역에 존재하는 레코드들은 클린 로그라고 부른다.
헤드 영역은 압축 전 레코드들을 의미하며 동일한 메시지 키를 가진 데이터가 존재할 수 있다. 이 영역에 존재하는 레코드들은 더티 로그라고 부른다.
결국 더티 영역 메시지 비율이 얼마나 되는지에 따라 데이터 압축 시점이 정해진다.
비율을 크게 설정하면 한 번에 압축되는 데이터의 양이 많지만, 압축이 될 때 까지 기다리면서 용량을 많이 차지하게 된다. 토픽별로 데이터 특성에 맞는 적절한 비율을 설정하는 것이 중요하다.
아래 그림을 보면 총 로그 크기에 대한 더티 로그의 비율이 얼마인지와 메시지 타임스탬프와 현재 시각을 비교해 min.compaction.lag.ms, max.compaction.lag.ms 기준을 넘는지에 따라 압축 시기가 결정됨을 알 수 있다.
리더 파티션과 팔로워 파티션이 모두 싱크된 상태를 의미한다.
팔로워 파티션이 리더 파티션의 데이터를 복제하기 까지 시간이 걸린다. 따라서 리더 파티션에 데이터가 적재된 후 팔로워 파티션에 즉시 복제되지 않아 오프셋 차이가 발생할 수 있다.
리더 파티션은 replica.lag.time.max.ms 속성값으로 입력된 주기마다 팔로워 파티션이 데이터를 복제한 상태인지 확인한다. 만약 이 때 데이터가 복제되지 않은 상태라면 팔로워 파티션에 문제가 생겼다고 판단하고 ISR 그룹에서 제외한다.
아래 그림과 같이 토픽의 파티션들이 브로커에 나뉘어 있고, 같은 오프셋을 가지고 있다면 하나의 ISR 그룹으로 묶이게 된다.
ISR로 묶인 파티션들 중 팔로워는 리더의 데이터를 그대로 담고 있으므로 만약 새로운 리더가 필요할 때 선출될 자격이 있다. 반면 ISR에 속하지 못한 팔로워는 데이터 유실 가능성이 크므로 새로운 리더로 선출될 수 없다.
만약 ISR 그룹에 팔로워 파티션이 없다면 리더 파티션이 재구동될 때 까지 기다려야 한다. 따라서 서비스가 중단될 수 있다.
만약 데이터가 유실되는 것을 감수하면서 서비스가 중단되지 않도록 하려면 unclean.leader.election.enable 속성을 true로 두어 ISR에 속하지 않은 팔로워 파티션도 리더 파티션으로 선출되도록 할 수 있다.
어떤 개발환경에서 사용되는지, 어떤 애플리케이션에서 어떤 데이터 타입으로 사용되는지 나타낼 수 있어야 한다.
다음과 같이 적합한 템플릿을 고안해 사용하면 좋다.
<환경>.<팀이름>.<애플리케이션이름>.<메시지타입>
ex) prod.marketing.sms-platform.json
<프로젝트이름>.<서비스이름>.<환경>.<이벤트이름>
ex) commerce.payment.prd.notification
토픽 이름을 변경할 수 없으므로 초기에 신중히 정해야 한다.
실질적으로 카프카에서 사용하는 데이터 단위이며 타임스탬프, 메시지 키, 메시지 값, 오프셋으로 구성되어 있다.
프로듀서가 생성하여 브로커로 전송하며, 한 번 브로커에 적재되면 수정이 불가능하다.
레코드가 브로커에 전송되면 타임스탬프(unix time)와 오프셋이 지정된다.
메시지 키는 메시지 값을 순서대로 처리하거나 메시지 값의 종류를 나타내기 위해 사용한다. 또한 프로듀서가 토픽에 레코드를 전달할 때 메시지 키의 해시 값을 이용해 파티션을 할당한다.
메시지 값에는 처리할 데이터가 들어가는데, 브로커에는 직렬화된 데이터가 들어오므로 컨슈머는 이를 역직렬화하여 사용해야 한다.
레코드의 오프셋은 0 이상의 숫자이며 직접 지정할 수 없고 브로커에 저장될 때 이전 레코드의 오프셋 값 + 1로 할당된다.
출처
아파치 카프카 애플리케이션 프로그래밍 책