StarRocks 구조와 최적화: 저장 방식·인덱스·Materialized View

anlee·2025년 6월 14일

매일매일 블로그

목록 보기
28/49

컴퓨팅 성능의 발전은 데이터 처리 패러다임의 변화를 이끌고 있습니다. 전통적인 ETL(Extract, Transform, Load) 방식에서, 데이터를 먼저 적재한 후 필요에 따라 변환하는 ELT(Extract, Load, Transform) 방식으로의 전환이 가속화되고 있죠. 이러한 변화 속에서 Apache Iceberg와 같은 오픈 테이블 형식과 스키마 진화(Schema Evolution) 기술이 등장했으며, 대규모 데이터를 빠르고 유연하게 분석할 수 있는 고성능 OLAP(Online Analytical Processing) 엔진의 중요성은 그 어느 때보다 커졌습니다.

이러한 배경 속에서 최근 주목받고 있는 데이터베이스가 바로 StarRocks입니다. 이 글에서는 StarRocks가 왜 현대 데이터 아키텍처에 필요한지, 그 내부 구조는 어떻게 동작하는지, 그리고 실제 운영 환경에서 성능을 극대화하기 위한 최적화 전략은 무엇인지 깊이 있게 살펴보겠습니다.


1. 왜 새로운 OLAP 엔진이 필요한가? ELT 시대의 도래

과거 빅데이터 분석 환경에서는 모든 데이터를 하나의 거대한 테이블에 미리 가공하여 넣는 플랫 테이블(Flat Table) 구조가 일반적이었습니다. 하지만 거버넌스 환경으로의 전환이 가속화되면서, 데이터가 목적에 맞게 잘게 분리되어 유통되는 경우가 많아졌습니다. 이로 인해 여러 데이터 소스를 유연하게 JOIN하여 분석하는 능력의 중요성이 커졌습니다.

또한, 고성능 OLAP 데이터베이스를 도입하면, 데이터베이스 내에서 직접 중간 및 최종 집계 데이터를 생성(롤업)하고, 세분화된 원본 데이터를 다양하게 변환하는 작업이 가능해집니다. 이는 데이터 파이프라인의 복잡성을 줄이고, 분석 속도를 높이는 데 큰 이점을 제공합니다.

◼️ OLAP 엔진 플랫폼 비교: StarRocks vs. ClickHouse

고성능 OLAP 엔진의 대표 주자로는 StarRocks와 ClickHouse가 있습니다. 두 데이터베이스는 여러 공통점을 가지지만, 핵심적인 차이점도 존재합니다.

비교 기준ClickHouseStarRocks
적재 후 가공테이블 엔진·쿼리·Materialized View생성 컬럼·쿼리·Materialized View
Materialized View삽입 블록을 처리하는 incremental 방식과 주기적으로 쿼리를 실행하는 refreshable 방식적재와 함께 유지되는 동기 MV와 별도 갱신하는 비동기 MV
저장·실행 구조MergeTree 계열의 정렬된 데이터 파트와 백그라운드 병합; 배포 방식별 저장 구조 확인BE 로컬 저장 또는 CN과 원격 저장소를 분리하는 구조
JOIN·집계데이터 크기·분포·알고리즘·버전별 측정 필요데이터 크기·분포·분산 방식·실행 계획별 측정 필요
선택 기준대표 쿼리의 지연·동시성·메모리·적재 비용대표 쿼리의 지연·동시성·메모리·적재 비용

ClickHouse의 JOIN이 항상 제한적이거나 StarRocks의 GROUP BY가 항상 빠르다고 일반화할 수 없습니다. 비교하려면 제품 버전, 자원, 스키마, 쿼리, 데이터 분포와 캐시 상태를 공개해야 합니다. 서로 다른 MV 갱신 모델의 비용과 데이터 지연도 함께 봅니다. ClickHouse MV 문서, StarRocks MV 문서

◼️ StarRocks의 주요 강점 요약

  • 고성능 JOIN: 기존 빅데이터 분석의 고질적인 문제였던 플랫 테이블 구조에서 벗어나, 정규화된 테이블들을 유연하게 조인하여 분석할 수 있습니다.
  • 고성능 단일 테이블 쿼리: 벡터화 실행과 여러 인덱스를 활용합니다. 공개 벤치마크 결과를 그대로 적용하기보다 실제 워크로드로 확인해야 합니다.
  • 클라우드 네이티브 아키텍처: 연산(Compute)과 스토리지(Storage)를 분리할 수 있는 구조를 지원하여, 클라우드 환경에서 오토 스케일링, 비용 효율적인 운영이 가능합니다.
  • Materialized View를 통한 쿼리 가속: 스케줄러 기반의 Materialized View를 통해 데이터를 미리 집계하고 조인해 둠으로써, 복잡한 쿼리의 성능을 획기적으로 향상시킬 수 있습니다.
  • 데이터 레이크 연동 최적화: Apache Iceberg, Hudi, Delta Lake와 같은 오픈 테이블 형식과의 연동에 최적화되어 있어, 데이터 레이크에 저장된 데이터를 직접 빠르고 효율적으로 조회할 수 있습니다.
  • 데이터 캐싱 메커니즘: 외부 데이터(예: 데이터 레이크)를 StarRocks 클러스터 내의 로컬 스토리지에 캐싱하여, 반복적인 쿼리 성능을 크게 향상시킬 수 있습니다.

2. StarRocks 내부 아키텍처: 어떻게 동작하는가?

StarRocks는 크게 프런트엔드(Frontend, FE)와 백엔드(Backend, BE/CN) 노드로 구성된 MPP(Massively Parallel Processing) 아키텍처를 가집니다.

  • 프런트엔드 (Frontend, FE): 클러스터의 두뇌
    • 역할: 메타데이터 관리, 쿼리 계획 수립 및 최적화, 스케줄링 등을 담당합니다.
    • 메타데이터 관리: 테이블 스키마, 파티션 정보, 데이터 위치 등 클러스터의 모든 메타데이터를 관리합니다.
    • 쿼리 최적화: CBO(Cost-Based Optimizer)를 사용하여 수신된 SQL 쿼리에 대한 가장 효율적인 실행 계획을 생성합니다.
    • 고가용성: 여러 FE 노드로 구성되며, 그중 하나가 리더(Leader)가 되어 메타데이터 쓰기 작업을 전담합니다. 다른 팔로워(Follower) 노드들은 Raft 프로토콜을 통해 리더의 메타데이터 업데이트를 동기화하여 고가용성을 보장합니다.
    • 주의사항: FE는 메타데이터를 메모리에 유지하므로, 클러스터 규모가 커질수록 충분한 JVM 메모리를 확보하는 것이 중요합니다. (수직적 스케일링 필요)
  • 백엔드 (Backend, BE / Compute Node, CN): 실제 데이터 처리 및 저장
    • 역할: 실제 데이터 저장 및 쿼리 실행을 담당하는 작업자 노드입니다.
    • BE (Backend Node): 네이티브 스토리지(로컬 디스크)에 데이터를 직접 저장하고, 해당 데이터에 대한 쿼리 연산을 수행합니다.
    • CN (Compute Node): 연산과 스토리지가 분리된 환경에서 연산(Compute) 역할만을 수행합니다. 데이터는 S3와 같은 외부 분산 스토리지에 저장되며, CN은 이 데이터를 읽어와 쿼리를 처리합니다.

◼️ Shared-nothing vs. Shared-data 구조

StarRocks는 배포 시나리오에 따라 두 가지 주요 아키텍처를 선택할 수 있습니다.

  • Shared-nothing 구조: 각 BE 노드가 자신만의 로컬 디스크에 데이터를 소유하고 처리하는 전통적인 MPP 구조입니다. 데이터 지역성(Data Locality)을 최대한 활용하여 높은 성능을 낼 수 있습니다.
  • Shared-data 구조 (연산/스토리지 분리): 데이터는 S3와 같은 중앙 분산 스토리지에 저장되고, CN 노드들은 이 데이터를 공유하여 연산을 수행합니다. 연산과 스토리지를 독립적으로 확장할 수 있어 유연성과 비용 효율성이 높으며, 클라우드 환경에 매우 적합합니다. 이 구조에서는 BE 대신 CN이 쿼리 실행과 로컬 캐시를 담당합니다. 원격 저장소를 쓰더라도 캐시 미스의 지연과 캐시 공간 관리 비용은 남습니다. StarRocks 아키텍처

🤔 꼬리 질문: Shared-nothing과 Shared-data 아키텍처는 각각 어떤 장단점을 가질까요? 어떤 비즈니스 요구사항이나 운영 환경에서 각 아키텍처가 더 유리할지 논의해 볼 수 있을까요?


3. StarRocks 내부 메커니즘: 성능의 비밀을 찾아서

StarRocks의 뛰어난 성능은 내부적으로 효율적인 스토리지 엔진과 데이터 레이아웃 설계에 기인합니다.

◼️ LSM-Tree 기반 스토리지 엔진

StarRocks는 LSM-Tree(Log-Structured Merge Tree) 기반의 스토리지 엔진을 사용합니다.

  • 동작 원리: 인입된 데이터는 먼저 메모리 내의 정렬된 버퍼(Memtable)에 기록된 후, 정렬된 상태 그대로 작은 단위의 불변 파일(SSTable, StarRocks에서는 세그먼트)로 디스크에 순차적으로 기록됩니다. 이렇게 잘게 나뉜 세그먼트들은 백그라운드에서 지속적인 병합 과정(컴팩션, Compaction)을 거쳐 더 큰 단위의 정렬된 데이터로 통합됩니다.
  • 특징: 이 구조는 랜덤 쓰기를 순차 쓰기로 변환하여 매우 높은 쓰기 성능을 제공하며, 데이터 압축에도 유리합니다.
  • 정렬 키(Sort Key)의 중요성: 데이터가 정렬 키를 기준으로 저장되므로, SELECT 쿼리의 WHERE 조건절에 정렬 키 컬럼이 포함되면 필요한 데이터 범위만 빠르게 스캔하여 성능을 크게 향상시킬 수 있습니다. (Predicate Pushdown) 따라서 자주 사용되는 필터링 조건을 고려하여 정렬 키를 신중하게 선택하는 것이 매우 중요합니다.

◼️ 정렬 키와 Prefix 인덱스

StarRocks는 정렬된 데이터에서 특정 데이터를 빠르게 찾기 위해, 일정한 간격으로 '값:위치' 형식의 희소 인덱스(Sparse Index)을 생성합니다. 정렬 키는 여러 컬럼의 조합으로 생성될 수 있으며, 이 조합의 앞부분을 사용한 Prefix 인덱스로 데이터의 위치를 표현합니다.

  • Prefix 인덱스 조건: 정렬 키 전체가 아닌 인덱스에 포함되는 접두 필드의 제한입니다. 공식 문서
    • 조합된 인덱스의 최대 길이는 36바이트로 제한됩니다.
    • 최대 3개의 컬럼까지만 조합에 사용될 수 있습니다.
    • CHAR, VARCHAR, STRING과 같은 문자열 타입은 한 번만, 그리고 조합의 마지막에만 사용할 수 있습니다.

◼️ 데이터 레이아웃: 파티셔닝과 버킷팅(샤딩)

StarRocks 테이블의 데이터는 파티셔닝(Partitioning)과 버킷팅(Bucketing, 다른 시스템의 샤딩과 유사)을 통해 여러 노드에 분산되어 저장 및 처리됩니다.

  • 파티셔닝 (Partitioning):
    • 목적: 데이터를 논리적인 단위로 분할하여 관리 효율성을 높이고, 쿼리 시 불필요한 데이터를 스캔하지 않도록 데이터 프루닝(Data Pruning)을 가능하게 합니다.
    • 설계: 주로 시간(날짜, 주, 월 등)과 같이 쿼리에서 범위 조건으로 자주 사용되는 컬럼을 파티셔닝 키로 선택합니다. LSM-Tree 구조상, 파티션 조건을 옵티마이저가 활용할 수 있으면 읽을 파티션을 줄일 수 있습니다. 함수·형 변환 등으로 조건을 활용하지 못하면 기대한 프루닝이 일어나지 않을 수 있습니다.
  • 버킷팅 (Bucketing):
    • 목적: 데이터를 여러 개의 버킷(태블릿)으로 분할하고, 이 버킷들을 클러스터에서 분산 처리하여 병렬 처리 성능을 극대화하는 것입니다.
    • 설계: 버킷팅 키(분산 키)를 해시(Hash)하여 데이터를 각 버킷에 할당합니다. 데이터가 모든 노드에 고르게 분포되도록, 카디널리티가 높은 컬럼을 버킷팅 키로 선택하는 것이 중요합니다.

🤔 꼬리 질문: 파티셔닝과 버킷팅은 어떤 차이가 있을까요? 만약 버킷팅 키를 잘못 선택하여 데이터가 특정 노드로 쏠리는 '데이터 스큐(Data Skew)' 현상이 발생하면, 쿼리 성능에 어떤 영향을 미치며 이를 어떻게 해결할 수 있을까요?


4. StarRocks 성능 최적화 실전 전략 ⚡

StarRocks의 성능을 최대한으로 끌어올리기 위해서는 다음과 같은 전략들을 종합적으로 고려해야 합니다.

  1. 사용할 스토리지 타입에 맞는 최적화 전략 선택:
    • 네이티브 스토리지 (Shared-nothing): BE 노드가 데이터 샤딩을 전담하므로, 데이터가 각 BE에 고르게 분산되도록 버킷팅 키를 신중하게 설계하고, SELECT 쿼리 조건에 맞는 정렬 키를 설정하여 데이터 프루닝 효과를 극대화합니다.
    • 네이티브 스토리지 (Shared-data): 분산 스토리지(S3 등)의 높은 처리량을 활용하며, 자주 사용되는 데이터(Hot Data)를 CN의 로컬 디스크에 캐싱하여 성능을 개선합니다. 컴팩션을 통해 분산 스토리지상의 순차 I/O를 유도하여 스몰 파일 문제를 완화합니다.
    • 외부 카탈로그 (Iceberg, Hive 등): 외부 테이블의 파티션 구성, 쓰기 엔진의 정렬 방식과 파일 통계, StarRocks가 지원하는 조건 푸시다운을 확인하여 파일 시스템 레벨에서 읽는 데이터 양을 최소화합니다. 데이터 캐시를 적극적으로 활용하여 외부 스토리지 접근을 줄입니다.
  2. 쿼리 패턴을 고려한 파티셔닝, 버킷팅, 정렬 키 설정:
    • 파티셔닝 키: WHERE 절에서 범위 조건으로 자주 사용되는 날짜 컬럼 등을 사용하여 파티션 프루닝이 효과적으로 일어나도록 설계합니다.
    • 버킷팅 키: 데이터가 모든 노드에 최대한 균등하게 분산될 수 있도록 카디널리티가 높은 컬럼을 선택합니다.
    • 정렬 키: WHERE 절의 필터링 조건과 일치시켜, 필요한 데이터 블록만 읽도록 설정하여 I/O를 최소화합니다.
  3. 데이터 스큐(Data Skew) 확인 및 병목 경감:
    • SHOW PARTITIONS 및 SHOW TABLETS 명령어를 통해 각 파티션과 태블릿(버킷의 실제 저장 단위)에 저장된 데이터의 크기와 행 수를 주기적으로 확인합니다.
    • 특정 태블릿에 데이터가 몰려있다면, 버킷팅 키가 데이터를 균일하게 분산시키지 못하고 있다는 신호이므로, 버킷팅 키 변경을 고려해야 합니다.
  4. Materialized View 적극 활용:
    • 자주 사용되는 집계 쿼리, 복잡한 JOIN 결과, 반복적인 데이터 변환이 필요한 경우 Materialized View를 생성하여 미리 계산된 결과를 저장해 둘 수 있습니다.
    • StarRocks의 Materialized View는 단순히 쿼리 결과를 저장하는 것을 넘어, 원본 테이블에 대한 쿼리 시 옵티마이저가 자동으로 해당 Materialized View를 사용하여 쿼리를 재작성(Query Rewrite)함으로써, 원본 테이블에 대한 쿼리 성능까지 투명하게 개선하는 강력한 기능을 제공합니다. (적용 가능한 쿼리 형태와 갱신 시점, 재작성 조건은 버전별 문서와 실행 계획으로 확인)
  5. 대량 쓰기 작업을 위한 적절한 파티셔닝:
    • 테이블의 파티션 수가 너무 많아지면 FE 노드가 관리해야 할 메타데이터 양이 증가하여 메모리 부하를 유발할 수 있습니다. 대량의 데이터를 장기간에 걸쳐 지속적으로 쓰는 작업의 경우, 너무 잘게 쪼개기보다는 일(Day), 주(Week), 월(Month) 등 비즈니스 요구사항에 맞는 적절한 파티션 단위를 선택하는 것이 중요합니다.
  6. 모니터링 및 쿼리 분석:
    • StarRocks는 Grafana 대시보드 템플릿을 공식적으로 제공하므로, 이를 활용하여 클러스터 상태, 컴팩션 현황, 각 노드의 리소스 사용량 등을 지속적으로 모니터링해야 합니다.
    • AuditLoader와 같은 도구를 사용하여 느린 쿼리(Slow Query)를 분석하고, 실행 계획을 확인하여 병목 지점을 찾아 최적화합니다.

도입 검증에서 확인할 것

'잘' 운영하기 위해서는 다양한 기술적 선택지 중에서 서비스의 요구사항과 특성에 맞는 최선의 선택을 내리는 것이 중요합니다. 데이터베이스 기술은 본격적으로 클라우드 환경에 맞춰 진화하고 있으며, 단순히 기존의 온프레미스(On-premise) 구조를 클라우드로 옮기는 것을 넘어, 클라우드 네이티브 환경에서 새롭게 개발되고 있습니다.

StarRocks 역시 동적 스케일링, 오브젝트 스토리지를 활용한 연산/스토리지 분리 등 클라우드 환경에 최적화된 기능들을 지속적으로 발전시키고 있습니다. Kubernetes 환경에서는 Operator를 활용하여 배포 및 관리를 자동화할 수 있지만, 그만큼 업그레이드나 장애 발생 시 대응 방안을 사전에 철저히 수립하는 고민이 필요합니다.

이 글이 고성능 OLAP 엔진, 특히 StarRocks를 이해하고 실제 시스템에 도입하여 성능을 최적화하는 데 도움이 되었기를 바랍니다.

참고:

  • LSM-Tree (Log-Structured Merge Tree): '흩어져 있는 데이터 변경 사항을 메모리에서 정렬한 후, 이를 디스크에 순차적으로 기록하고, 백그라운드에서 여러 개의 작은 파일들을 하나의 큰 정렬된 파일로 병합하는 기술'로, 쓰기 성능을 극대화하기 위해 많은 데이터 저장 엔진(RocksDB, HBase, Cassandra 등)에서 사용되는 핵심 스토리지 엔진 구조입니다.
  • CBO (Cost-Based Optimizer): 데이터에 대한 통계 정보(테이블 크기, 카디널리티, 데이터 분포 등)를 기반으로 여러 가능한 쿼리 실행 계획들의 비용을 예측하고, 가장 비용이 적게 드는 최적의 실행 계획을 선택하는 쿼리 옵티마이저입니다. StarRocks는 강력한 CBO 성능을 주요 특징 중 하나로 내세우고 있습니다.

0개의 댓글