Apache Flink는 실시간 데이터 스트림 처리 엔진입니다. 대부분의 스트림 처리 애플리케이션은 ‘상태 유지형’입니다. 이는 상태가 저장되고 추가 처리를 위해 사용된다는 것을 의미합니다. Apache Flink에서는 구성된 상태 백엔드를 통해 상태를 관리합니다. Flink는 프로덕션에서 두 가지 상태 백엔드를 지원합니다. 하나는 HashMapStateBackend
이고, 다른 하나는 EmbeddedRocksDBStateBackend
입니다.
데이터 손실을 방지하고 내결함성을 달성하기 위해 Flink는 상태의 스냅샷을 내구성 있는 저장소에 지속할 수 있습니다. Flink는 전체 상태를 내구성 있는 위치에 스냅샷하거나 마지막 스냅샷 이후의 델타를 스냅샷하도록 구성할 수 있습니다. 전자는 전체 체크포인트라고 하고, 후자는 증분 체크포인트로 알려져 있습니다.
이 기사에서는 HashMapStateBackend
와 전체 체크포인트, EmbeddedRocksDBStateBackend
와 증분 체크포인트를 비교할 것입니다. 이 기사는 독자가 Apache Flink에 대한 실무 지식이나 이론적 지식을 가지고 있다고 가정합니다.
Flink 상태 백엔드 개요
Flink 상태 백엔드를 이해하려면 진행 중인 상태와 상태 스냅샷의 차이를 아는 것이 중요합니다.
진행 중인 상태는 Flink의 작업 상태로 알려져 있으며 로컬에 저장됩니다. 상태 백엔드 구성에 따라 힙 메모리 또는 오프 힙 메모리에 있으며, 로컬 디스크로의 스필오버가 발생할 수 있습니다.
한편, 상태 스냅샷(체크포인트 또는 저장 지점)은 지속적인 원격 위치에 저장됩니다. 이러한 스냅샷은 작업 실패 시 Flink 작업 상태를 재구성하는 데 사용됩니다.
작업이 실패할 경우 인-플라이트 상태가 손실될 수 있습니다. 그러나 작업에서 체크포인트가 활성화된 경우에는 작업 복구에 영향을 미치지 않습니다. 체크포인트가 구성되면 상태는 복구 시점에 지속적인 저장소에서 검색됩니다.
생산에 사용할 상태 백엔드를 선택하는 것은 응용 프로그램의 처리량, 지연 시간 및 확장성 요구 사항에 따라 다릅니다.
Apache Flink가 제공하는 두 가지 상태 백엔드가 있습니다.
1. HashMapStateBackend
이는 스트림 처리 중 Keyed State 및 Operator State를 관리하기 위한 Flink의 가벼운 상태 백엔드입니다. 상태는 HashMap 데이터 구조를 사용하여 Java Heap에 저장됩니다. 메모리에 저장되므로 여기서의 주요 제약 사항은 최대 상태 크기가 Java Heap 크기로 제한된다는 것입니다. 상태에 쓰거나 상태에서 읽을 때 직렬화가 포함되어 있지 않습니다. 따라서 낮은 지연 시간, 높은 처리량 및 크지 않은 상태 응용에 적합합니다.
2. EmbeddedRocksDBStateBackend
이 상태 백엔드는 미결 데이터를 인메모리 RocksDB 데이터베이스에 저장합니다. 기본적으로 RocksDB는 데이터를 작업 관리자의 로컬 디스크에 저장합니다. 데이터는 직렬화되어 오프힙 메모리에 저장되고 작업 관리자에 연결된 로컬 디스크로 스파일링됩니다. 직렬화 형식은 애플리케이션에서 구성된 유형 직렬화기에 따라 달라집니다.
이 상태 백엔드를 사용하면 저장할 수 있는 상태 양이 작업 관리자에 연결된 디스크 공간에 의해 제한됩니다. 애플리케이션이 큰 상태를 가지고 있고 힙 메모리에 포함될 수 없는 경우, 이것이 적절한 상태 백엔드입니다. 직렬화가 포함되므로 HashMapStateBackend
보다 높은 대기 시간과 낮은 처리량이 발생할 것입니다.
스냅샷 상태 개요
스냅샷은 Flink 작업의 전역 상태를 나타냅니다. 이는 각 데이터 소스에 대한 포인터와 해당 소스에서 포인터까지 처리된 후 Flink의 모든 상태 있는 연산자의 상태로 구성됩니다. Apache Flink에서의 체크포인팅은 주기적으로 상태를 내구성 있는 원격 저장소에 저장함으로써 내결함성을 달성하는 메커니즘입니다.
작업 실패 시, Flink는 내구성 있는 원격 저장소에서 저장된 상태를 검색하고 중단된 지점부터 스트리밍 데이터 처리를 다시 시작합니다. Flink는 비동기 바리어 스냅샷을 사용합니다. 이는 Chandy-Lamport 알고리즘의 변형입니다.
Flink는 두 가지 유형의 체크포인팅을 지원합니다.
1. 전체 체크포인팅
전체 체크포인팅은 Flink 작업의 전체 상태가 캡처되어 내구성 있는 원격 저장소에 저장되는 곳입니다. 작업 실패 시, 작업은 이전에 저장된 상태에서 복구됩니다. 저장 공간 요구 사항 및 체크포인팅에 소요되는 시간은 완전히 응용 프로그램 상태에 의존합니다. 전체 체크포인팅은 HashMapStateBackend
와 RocksDBStateBackend
모두에서 작동합니다.
2. 점진적 체크포인팅
점진적 체크포인팅은 최적화된 접근 방식입니다. 전체 상태를 스냅샷하는 대신, Flink는 마지막 체크포인트 이후 상태에 대한 ‘델타’만 저장합니다. 이는 네트워크 오버헤드와 따라서 체크포인팅에 소요되는 시간을 줄입니다. 이 경우 체크포인팅은 완전히 비동기로 이루어집니다.
RocksDBStateBackend
만이 점진적 체크포인팅을 지원합니다. Flink는 이를 위해 RocksDB의 내부 메커니즘을 활용합니다. 체크포인팅이 전체 체크포인팅보다 시간이 적게 소요되지만, 작업 실패 시 복구 시간은 여러 요소에 따라 달라집니다. 네트워크가 병목 현상이라면, 전체 체크포인팅보다 더 오랜 복구 시간이 소요될 수 있습니다.
분석
파이프라인 세부 정보: Apache Beam 파이프라인이 Flink 엔진에서 10분의 고정된 윈도우로 실행되며, 체크포인트는 3분마다 실행하도록 구성되어 있습니다. 구성된 직렬화 유형은 AVRO입니다.
- 클러스터 유형: “m5dn.4xlarge”
- 최종 체크포인트 저장소: S3
- 고유한 키 수: 2K
Input rate 10k/s (~ 7.8 MB/s) | |||||||||
---|---|---|---|---|---|---|---|---|---|
Type | No: TM의 |
병렬 처리 | 힙 할당 TM당 |
힙 사용량 TM당 |
파드 메모리 사용량 TM당 |
CPU 사용량 TM당 |
체크포인트 크기 |
체크포인트 지속 시간 |
플링크 관리 메모리 |
HashMapState 전체 체크포인트 완료 |
1 | 1 | 10GB | 8.5GB | 11.1GB | 1.2 | 4GB | 50초 | 0 |
RocksDBState 증분 체크포인트 AVRO와 함께 |
1 | 1 | 3GB | 1.82GB | 4.63GB | 1.5 | 207MB | 3초 | 3GB |
Input rate 20k/s (~15.6 MB/s) | |||||||||
---|---|---|---|---|---|---|---|---|---|
타입 | No: TM의 |
병렬 처리 | 힙 할당 TM당 |
힙 사용량 TM당 |
파드 사용량 TM당 |
CPU 사용량 TM당 |
체크포인트 크기 |
체크포인트 지속 시간 |
플링크 관리 메모리 |
HashMapState 전체 체크포인트 |
2 | 2 | 10GB | 8.69GB | 11.2GB | 1.3 | 8.39GB | 50초 | 0 |
RocksDBState 증분 체크포인트와 AVRO |
2 | 2 | 3GB | 1.87GB | 4.71GB | 1.4 | 404MB | 3초 | 3GB |
input rate 30k/s (~23.5 MB/s) | |||||||||
---|---|---|---|---|---|---|---|---|---|
유형 | 번호: TM 수 |
병렬 처리 | 힙 할당 TM당 |
힙 사용량 TM당 |
Pod 사용량 TM당 |
CPU 사용량 TM당 |
체크포인트 크기 |
체크포인트 지속 시간 |
Flink 관리 메모리 |
HashMapState 전체 체크포인트와 함께 |
3 | 3 | 10GB | 9.74GB | 11.2GB | 1.2 | 11.8GB | 65초 | 0 |
RocksDBState 증분 체크포인트와 AVRO |
3 | 3 | 3GB | 1.72GB | 3.75GB | 1.4 | 576MB | 5초 | 3GB |
위 실험에서 볼 수 있듯이, 체크포인트 지속 시간은 증분 체크포인트 방식을 사용하면 감소합니다. 이는 애플리케이션 성능에 매우 도움이 될 수 있습니다.
요약
아래는 실험의 요약입니다.
HashMapStateBackend와 전체 체크포인트 | 증분 체크포인트가 있는 RocksDBStateBackend | |
응용 프로그램 지연 시간 | 데이터가 힙에 Java 객체로 저장되기 때문에 낮은 지연 시간. 읽기 및 쓰기에는 직렬화가 포함되지 않습니다. | 모든 읽기 또는 쓰기 응용 프로그램에 직렬화가 포함되므로 지연 시간이 더 높아집니다. |
확장성 | 상태가 큰 작업에 대해 확장성이 낮음 | 상태가 크고 서서히 변화하는 작업에 대해 확장성이 높음 |
장애 내성 | 매우 장애 내성 있음 | 매우 장애 내성 있음 |
체크포인트 지속 시간 | 매번 전체 데이터 세트에 대한 스냅샷이 발생하기 때문에 체크포인트 지속 시간이 높음. | 마지막 체크포인트 이후의 델타만 저장되므로 체크포인트 지속 시간이 짧음. |
복구 복잡성 | 하나의 스냅샷만 로드하면 되므로 복구가 용이함. | 복구가 복잡하며 RocksDB가 여러 체크포인트에서 상태를 구축해야 하며, 네트워크 속도에 많이 의존함. |
저장 요구 사항 | HashMapStateBackend와 RocksDBStatebackend 모두에서 지원됨. | RocksDBStatebackend에서만 지원됨. |
상태 스냅샷 | 모든 체크포인트에서 전체 상태를 저장함. | 마지막 성공적인 체크포인트 이후의 델타만 저장함. |
힙 크기 | 체크포인트 전에 상태가 힙에 저장되므로 힙 요구 사항이 높고, 더 많은 GC 주기가 예상됨. | 상태는 오프-힙에 저장되며 로컬 디스크에도 저장될 수 있으므로 힙 공간이 적고 GC 주기도 적어집니다. |
상태 백엔드 크기 | JVM에 할당된 최대 힙으로 제한됩니다. | RocksDB 상태 백엔드 크기는 JVM 힙 제한이 아닌 가능한 디스크 공간에만 제한됩니다. |
성능 영향 | 전체 스냅샷이기 때문에 처리에 더 큰 영향이 있습니다. | 델타만 스냅샷되므로 처리에 더 적은 영향이 있습니다. |
CPU | CPU 사용량은 처리 및 GC에만 해당됩니다. 상태 백엔드 직렬화는 관련이 없습니다. | 전체 체크포인트와 비교했을 때 CPU 사용량이 높습니다.
적절한 직렬화 메커니즘을 적용하여 CPU 이용률을 최적화할 수 있습니다. Avro를 사용한 실험 결과 Kryo보다 훨씬 좋은 결과를 얻었습니다. |
최적 사용 사례 | 작은 상태 백엔드 크기와 빈번히 변경되는 상태에 적합합니다. | 큰 상태 백엔드 및 천천히 업데이트되는 상태에 적합합니다. |
Source:
https://dzone.com/articles/apache-flink-full-checkpoint-vs-incremental-checkpoint