주요 컨텐츠로 이동

Apache Spark Structured Streaming에서 상태 유지(stateful) 파이프라인의 최신 성능 향상에 대한 심층 분석

Mojgan Mazouchi
Mrityunjay Kumar
Anish Shrigondekar
Karthikeyan Ramasamy
이 포스트 공유하기

(번역: Youngkyong Ko) Original Post

이 글은 상태 유지(stateful) 파이프라인의 최신 성능 개선에 대해 2부로 구성된 시리즈 중 두 번째 파트입니다. 이 글을 읽기 전에 이 시리즈의 첫 번째 파트인 Apache Spark Structured Streaming의 상태 유지 파이프라인 성능 개선을 먼저 읽어보시는 것이 좋습니다.

우리는 Lightspeed 프로젝트 업데이트 블로그에서 상태 유지 파이프라인을 위해 추가된 다양한 성능 개선 사항에 대해 개괄적으로 설명했습니다. 이 섹션에서는 성능을 분석하면서 관찰한 다양한 문제를 자세히 살펴보고 이러한 문제를 해결하기 위해 구현한 구체적인 개선 사항을 간략하게 설명합니다.

RocksDB State Store Provider의 개선 사항

메모리 관리

RocksDB는 주로 memtable, 블록 캐시 그리고 기타 고정된 블록을 위해 메모리를 사용합니다. 이전에는 마이크로 배치 내의 모든 업데이트가 WriteBatchWithIndex를 사용하여 메모리에 버퍼링되었습니다. 또한 사용자는 쓰기 버퍼 및 블록 캐시 사용에 대한 개별 인스턴스 메모리 제한만 구성할 수 있었습니다. 이 설정에서는 인스턴스 단위로 메모리를 제한없이 사용할 수 있었고, 이 때문에 단일 워커 노드에 여러 상태 저장소 인스턴스가 스케줄된 경우 문제가 더욱 복잡해졌습니다.

이러한 문제를 해결하기 위해 이제 사용자가 RocksDB의 쓰기 버퍼 관리자 기능을 활용하여 메모리 사용 제한을 강제할 수 있게 했습니다. 이를 통해 사용자는 단일 전역 메모리 제한을 설정하여 단일 executor 노드의 상태 저장소 인스턴스에서 블록 캐시, 쓰기 버퍼, 필터 블록 메모리 사용을 제어할 수 있습니다. 또한, 더 이상 업데이트가 무제한으로 버퍼링되지 않고 데이터베이스에 직접 쓰여지도록 WriteBatchWithIndex에 대한 의존성을 완전히 제거했습니다.

Database Write/Flush 성능

최신 개선 사항에서는 모든 업데이트가 로컬에 SST 파일로 안전하게 기록되고 이후 각 마이크로 배치에 대한 체크포인트 디렉터리의 일부로 영구 저장소에 백업되므로 더 이상 명시적으로 write ahead log (WAL)가 필요하지 않습니다.

Architecture with WAL
Architecture with WAL

Updated Architecture
Updated Architecture

이 변경으로 모든 읽기 및 쓰기를 주로 메모리에서 제공할 뿐만 아니라, changelog 체크포인트가 활성화되면 각 마이크로 배치가 아닌 주기적으로 스토리지에 쓰기를 플러시할 수 있게 되었습니다.

Changelog Checkpointing

상태 기반 스트리밍 쿼리의 주요 성능 병목 지점 중 하나는 상태 체크포인팅 지연 시간입니다. 이 지연 시간은 백그라운드 작업과 관련된 RocksDB 인스턴스의 주기적인 일시 중지, 그리고 배치 커밋 시의 스냅샷 생성과 업로드 프로세스에서 비롯됩니다.

새로운 디자인에서는 더 이상 전체 상태를 체크포인트 위치로 스냅샷할 필요가 없습니다. 대신 changelog checkpointing을 활용하여 각 마이크로 배치 커밋의 마지막 체크포인트 이후의 변경 사항만 저장함으로써 마이크로 배치의 상태를 영구적으로 유지합니다.

또한 스냅샷 프로세스는 이제 업데이트를 수행하는 동일한 데이터베이스 인스턴스에서 처리되며, 스냅샷은 태스크 실행을 블럭하지 않도록 백그라운드 유지 관리 작업을 사용하여 비동기식으로 업로드됩니다. 이제 사용자는 스냅샷 간격을 유연하게 구성하여 장애 복구와 리소스 사용 간의 균형을 맞출 수 있습니다. 스냅샷을 선택하고 해당 스냅샷 이후에 생성된 변경 로그를 재생하여 모든 버전의 상태를 재구성할 수 있습니다. 이를 통해 RocksDB state store provider를 사용하여 더 빠르게 상태를 체크포인팅할 수 있습니다.

다음 그림은 새로운 메커니즘의 작동 방식을 보여줍니다.

Changelog commit, with async snapshot uploads
Step 1. Changelog commit, with async snapshot uploads. 

Version reconstruction
Step 2. Version reconstruction. To load version j, load the latest snapshot i before j, then replay i+j to j version changelog.

Periodic snapshotting with background uploads
Step 3. Periodic snapshotting with background uploads.

Sink 관련 개선 사항

상태 기반 작업이 완료되면 commit을 호출하여 상태가 상태 저장소에 저장됩니다. 상태가 성공적으로 저장되면 파티션 데이터(executor의 데이터 조각)를 싱크에 기록해야 합니다. executor는 드라이버의 output commit coordinator와 통신하여 다른 executor가 동일한 데이터 조각에 대한 결과를 커밋하지 않았는지 확인합니다. 다른 executor가 이 파티션에 커밋하지 않았다는 것을 확인한 후에만 커밋을 진행할 수 있으며, 그렇지 않으면 exception을 내면서 작업이 실패합니다.

이 구현으로 인해 원치 않는 RPC 지연이 발생했는데, "at-least-once" 시맨틱만 제공하는 싱크의 경우 이를 쉽게 우회할 수 있다고 판단했습니다. 새로운 구현에서는 at-least-once 시맨틱을 사용하는 모든 DataSource V2(DSv2) 싱크에 대해 이 동기식 단계를 제거하여 지연 시간을 개선했습니다. 한가지 유의할 점으로, end-to-end exactly-once 파이프라인은 재생 가능한 소스와 멱등성을 갖는 싱크의 조합을 사용하며, 이를 통해 시맨틱 보장은 변경되지 않습니다. 

Operator와 Maintenance 관련 작업 개선

프로젝트 Lightspeed의 일환으로 스트림-스트림 조인 쿼리와 같은 특정 유형의 연산자에 대한 개선도 이루어졌습니다. 이러한 쿼리의 경우, 이제 파티션과 관련된 모든 인스턴스에 대해 상태 저장소의 병렬 커밋을 지원하여 지연 시간을 개선합니다.

또 다른 개선 사항은 주로 만료된 상태를 스냅샷하고 정리하는 백그라운드 maintenance 작업과 관련된 것입니다. 이 작업이 제대로 수행되지 않으면 많은 수의 delta/changelog 파일이 누적되어 재생 속도가 느려질 수 있습니다. 이를 방지하기 위해 이제 만료된 상태의 삭제를 병렬로 수행하고 maintenance 작업을 스레드 풀의 일부로 실행함으로써, 단일 executor 노드에서 로드된 모든 상태 저장소 인스턴스를 서비스하는 단일 스레드에 병목 현상이 발생하지 않도록 지원합니다.

결론

상태 유지 Structured Streaming 파이프라인에서 이러한 최신 개선 사항을 사용해 보시길 권장합니다. 우리는 Lightspeed 프로젝트의 일환으로 모든 스트리밍 파이프라인의 처리량과 지연 시간을 개선하여 TCO를 낮추는 데 주력하고 있습니다. 이 영역에 대한 더 많은 업데이트가 곧 있을 예정이니 기대해 주세요!

가용성

위에 언급된 모든 기능은 DBR 13.3 LTS 릴리스부터 사용할 수 있습니다.

Databricks 무료로 시작하기

관련 포스트

모든 엔지니어링 블로그 포스트 보기