Pinot 스토리지 모델

Pinot 스토리지 모델 (Pinot Storage Model)

Apache Pinot™이 데이터를 저장하는 방식을 설명하는 핵심 개념 페이지예요. 테이블, 세그먼트, 테넌트, 클러스터라는 네 가지 추상화가 어떻게 데이터를 모델링하고 격리하며 관리하는지 이해하면, Pinot의 분산 아키텍처가 어떻게 동작하는지 전체 그림이 잡혀요.

출처: Pinot Storage Model

본문

Apache Pinot™은 데이터 저장을 모델링하는 추상화나 시스템의 기능을 구동하는 인프라 구성 요소를 가리키는 다양한 용어를 사용해요. 여기에는 다음이 포함돼요:

Pinot는 수평 확장되는 분산 시스템 아키텍처를 가져요. Pinot는 테이블의 크기가 시간이 지남에 따라 무한히 커질 것으로 기대해요. 이를 달성하려면 모든 데이터를 여러 노드에 분산해야 해요. Pinot는 데이터를 세그먼트(HA 관계형 데이터베이스의 샤드/파티션과 유사)로 알려진 더 작은 청크로 나눠서 이를 달성해요. 세그먼트는 시간 기반 파티션으로도 볼 수 있어요.

테이블 (Table)

전통적인 데이터베이스와 유사하게 Pinot에도 테이블의 개념이 있어요. 관련된 데이터 모음을 가리키는 논리적 추상화죠. 관계형 데이터베이스 관리 시스템(RDBMS)과 마찬가지로 테이블은 SQL로 쿼리되는 칼럼과 행(문서)으로 구성된 구조예요. 테이블은 테이블의 칼럼과 그 데이터 타입을 정의하는 스키마와 연결돼요.

RDBMS 스키마와 달리 Pinot에서는 단일 스키마 정의를 상속하는 여러 테이블(실시간 또는 배치)을 만들 수 있어요. 테이블은 인덱싱 전략, 파티셔닝, 테넌트, 데이터 소스, 복제 같은 사항에 대해 독립적으로 구성돼요.

Pinot는 데이터를 테이블에 저장해요. Pinot 테이블은 개념적으로 행과 칼럼이 있는 관계형 데이터베이스 테이블과 동일해요. 칼럼은 같은 이름과 데이터 타입을 가지며, 이를 테이블의 스키마라고 해요.

Pinot 스키마는 JSON 파일로 정의돼요. 스키마 정의가 별도 파일에 있기 때문에 여러 테이블이 단일 스키마를 공유할 수 있어요. 각 테이블은 고유한 이름, 인덱싱 전략, 파티셔닝, 데이터 소스 및 기타 메타데이터를 가질 수 있어요.

Pinot 테이블 유형은 다음과 같아요:

  • real-time: Apache Kafka® 같은 스트리밍 소스에서 데이터를 수집
  • offline: 배치 소스에서 데이터를 로드
  • hybrid: 배치 소스와 스트리밍 소스 둘 다에서 데이터를 로드

세그먼트 (Segment)

Pinot 테이블은 세그먼트라고 하는 하나 이상의 독립적인 샤드에 저장돼요. 작은 테이블은 단일 세그먼트에 담길 수 있지만, Pinot는 테이블이 무제한의 세그먼트로 커질 수 있게 해요. 세그먼트를 생성하는 과정은 다양해요 (수집 참조). 세그먼트는 테이블 데이터의 시간 기반 파티션이며, 스토리지와 컴퓨팅 모두에서 필요에 따라 수평 확장되는 Pinot 서버에 저장돼요.

테넌트 (Tenant)

멀티 테넌시를 지원하기 위해 Pinot는 테넌트를 일급 기능(first class)으로 지원해요. 테이블은 테넌트와 연결돼요. 이를 통해 특정 논리 네임스페이스에 속하는 모든 테이블이 단일 테넌트 이름 아래 그룹화되고 다른 테넌트와 격리될 수 있어요. 테넌트 간 격리는 애플리케이션과 팀에 서로 다른 네임스페이스를 제공해서 테이블이나 스키마를 공유하는 것을 방지해요. 애플리케이션을 구축하는 개발 팀은 독립적인 Pinot 배포를 운영할 필요가 없어요. 조직은 단일 클러스터를 운영하고 새 테넌트가 전체 쿼리 볼륨을 늘리면서 확장할 수 있어요. 개발자는 클러스터의 다른 테넌트에 영향을 받지 않고 자신의 스키마와 테이블을 관리할 수 있어요.

모든 테이블은 테넌트, 즉 클러스터가 테이블에 대한 쿼리를 처리하는 위치를 제한하는 논리 네임스페이스와 연결돼요. Pinot 테넌트는 논리 테넌트 네임스페이스의 텍스트 태그 형태를 취해요. 물리적 클러스터 하드웨어 리소스(즉, 브로커와 서버)도 공통 테넌트 네임스페이스의 테넌트 태그와 연결돼요. 특정 테넌트 태그의 테이블은 같은 테넌트 태그에 속하는 하드웨어 리소스에서만 스토리지와 쿼리 처리를 위해 스케줄링돼요. 이를 통해 Pinot 클러스터 운영자는 특정 워크로드를 특정 하드웨어 리소스에 할당해서, 서로 다른 워크로드의 데이터가 같은 물리 하드웨어에 저장되거나 처리되는 것을 방지할 수 있어요.

기본적으로 모든 테이블, 브로커, 서버는 DefaultTenant라는 테넌트에 속하지만, Pinot 클러스터에서 여러 테넌트를 구성할 수 있어요.

클러스터 (Cluster)

Pinot 클러스터는 데이터를 수집, 저장, 처리하는 데 필요한 소프트웨어 프로세스와 하드웨어 리소스의 모음이에요. Pinot 클러스터 구성 요소에 대한 자세한 내용은 물리적 아키텍처를 참조하세요.

물리적 아키텍처 (Physical architecture)

Pinot 아키텍처 다이어그램

Pinot 클러스터는 일반적으로 프로덕션에서는 별도의 하드웨어 리소스에 배포되는 다음 프로세스들로 구성돼요. 개발에서는 일반적인 노트북의 Docker 컨테이너에 충분히 들어갑니다.

  • Controller: 클러스터 메타데이터를 유지하고 클러스터 리소스를 관리해요.
  • Zookeeper: controller를 대신해 Pinot 클러스터를 관리해요. 테이블 설정, 스키마, 세그먼트 메타데이터, 클러스터 상태를 포함한 메타데이터의 내결함성(fault-tolerant), 영구 저장을 제공해요.
  • Broker: 클라이언트 프로세스에서 쿼리를 받아 처리를 위해 서버로 전달해요.
  • Server: 세그먼트 파일의 스토리지와 쿼리 처리를 위한 컴퓨팅을 제공해요.
  • (선택) Minion: 쿼리 처리를 제외한 백그라운드 작업을 계산해서 쿼리 지연에 미치는 영향을 최소화해요. 세그먼트를 최적화하고 추가 인덱스를 구축해 (데이터가 삭제되더라도) 성능을 보장해요.

가장 단순한 Pinot 클러스터는 서버, 브로커, controller, Zookeeper 노드의 네 가지 구성 요소로 구성돼요. 프로덕션 환경에서는 이 구성 요소들이 일반적으로 별도의 서버 인스턴스에서 실행되고, 데이터 볼륨, 부하, 가용성, 지연에 따라 필요에 따라 확장돼요. 프로덕션 Pinot 클러스터는 10개 미만의 총 인스턴스에서 1,000개 이상까지 다양해요.

Pinot는 분산 메타데이터 저장소로 Apache Zookeeper를, 클러스터 관리를 위해 Apache Helix를 사용해요.

Helix는 Pinot의 제작자가 만든 클러스터 관리 솔루션이에요. Helix는 Pinot 클러스터의 의도된 상태에 대한 영구적이고 내결함성 있는 맵을 유지해요. 현재 구성을 구현하기 위해 올바른 하드웨어 리소스가 할당되었는지 지속적으로 모니터링해요. 구성이 변경되면 Helix는 새 구성을 반영하도록 하드웨어 리소스를 스케줄링하거나 해제해요. 클러스터의 요소가 치명적으로 상태를 변경하면 Helix는 메타데이터에 나타난 이상적인 상태와 실제 클러스터를 일치시키도록 하드웨어 리소스를 스케줄링해요. 물리적 관점에서 Helix는 controller 프로세스에 더해 서버와 브로커에서 실행되는 에이전트의 형태를 취해요.

Controller

Controller는 Pinot 클러스터의 일관성과 라우팅을 구동하는 핵심 오케스트레이터예요. Controller는 독립 컴포넌트(컨테이너)로 수평 확장되며 클러스터의 다른 모든 컴포넌트의 상태를 볼 수 있어요. Controller는 시스템의 상태 변화에 반응하고 응답하며 테이블, 세그먼트, 노드에 대한 리소스 할당을 스케줄링해요. 앞서 언급했듯이 Helix는 controller 내에 에이전트로 내장되어 있어, 다른 컴포넌트가 구독하는 상태 변화를 관찰하고 구동하는 참여자 역할을 해요.

Pinot controller는 메타데이터가 변경되거나 노드가 실패할 때 Pinot 클러스터의 리소스를 스케줄링하고 재스케줄링해요. Apache Helix Controller로서 클러스터를 구성하는 리소스를 스케줄링하고 특정 외부 프로세스와 클러스터 컴포넌트 간의 연결을 오케스트레이션해요(예: 실시간 테이블과 오프라인 테이블의 수집). 자체 서버의 단일 프로세스로 배포하거나 액티브/패시브 구성의 중복 서버 그룹으로 배포할 수 있어요.

Controller는 클러스터 전체 관리 작업을 위한 REST API 엔드포인트와 대화형 SQL 쿼리 및 간단한 관리 작업을 실행하는 웹 기반 쿼리 콘솔을 노출해요.

Server

서버는 여러 노드에 스케줄링되고 할당되며 테넌트의 할당에 따라 라우팅되는 세그먼트(샤드)를 호스팅해요(기본적으로 단일 테넌트가 있음). 서버는 수평 확장되는 독립 컨테이너이며 controller가 구동하는 상태 변경을 통해 Helix가 알려줘요. 서버는 실시간 서버 또는 오프라인 서버가 될 수 있어요.

실시간 서버와 오프라인 서버는 리소스 사용 요구 사항이 매우 달라요. 실시간 서버는 외부 시스템(예: Kafka 토픽)에서 새 메시지를 지속적으로 소비하며, 메시지는 테넌트의 세그먼트에 수집되고 할당돼요. 이 때문에 리소스 격리를 사용해 수집되어 브로커를 통해 쿼리 가능하게 되는 고처리량 실시간 데이터 스트림을 우선시할 수 있어요.

Broker

Pinot 브로커는 클라이언트 프로세스에서 쿼리 요청을 받아 적용 가능한 서버로 흩뿌리고, 결과를 모아 클라이언트에 반환해요. Controller는 브로커와 클러스터 메타데이터를 공유해서 브로커가 소스 데이터를 가진 서버의 최소 하위 집합과, 필요 시 결과를 셔플하고 통합할 다른 서버를 포함하는 쿼리 실행 계획을 세울 수 있게 해요.

프로덕션 Pinot 클러스터에는 많은 브로커가 있어요. 일반적으로 브로커가 많을수록 클러스터가 처리할 수 있는 동시 쿼리가 많아지고 쿼리에 더 낮은 지연을 제공할 수 있어요.

Pinot Minion

Pinot minion은 GDPR(일반 데이터 보호 규정)을 위한 "purge" 같은 백그라운드 작업을 실행하는 데 사용할 수 있는 선택적 컴포넌트예요. Pinot는 불변 집계 저장소이므로 민감한 개인 데이터를 포함한 레코드는 요청별로 제거(purge)해야 해요. Minion은 데이터 삭제 가능성의 존재 속에서도 성능을 보장하는 추가 인덱스를 구축하고 Pinot 세그먼트를 최적화하면서 GDPR을 준수하는 이 목적을 위한 솔루션을 제공해요. 주기적으로 실행되는 커스텀 작업도 작성할 수 있어요. 이러한 작업을 Pinot 서버에서 직접 수행하는 것도 가능하지만, 별도 프로세스(Minion)를 두면 세그먼트가 변경 가능한 쓰기의 영향을 받으면서 쿼리 지연이 전체적으로 저하되는 것을 줄일 수 있어요.

Pinot minion은 브로커와 서버가 수행하는 쿼리 프로세스와 별개로 테이블 데이터에 대한 백그라운드 작업을 실행하는 선택적 클러스터 컴포넌트예요. Minion은 독립 하드웨어 리소스에서 실행되며 controller가 지시하는 minion 태스크를 실행할 책임이 있어요. minion 태스크의 예로는 Avro나 JSON 같은 표준 형식의 배치 데이터를 오프라인 테이블에 로드할 세그먼트 파일로 변환하는 것, GDPR 같은 데이터 개인정보 보호법이 요구하는 대로 레코드를 제거하기 위해 기존 세그먼트 파일을 다시 쓰는 것이 있어요. Minion 태스크는 한 번 실행하거나 주기적으로 실행되도록 스케줄할 수 있어요.

Minion은 대역 외(out-of-band) 데이터 처리의 계산 부담을 서버로부터 격리해요. Pinot 클러스터는 minion이 있든 없든 기능하지만, 일반적으로 배치 데이터 수집 같은 일상적인 작업을 지원하기 위해 존재해요.

더 알아보기 (Learn more)