LLM WikiAccess-protected knowledge portal

WIKI

Trino 아키텍처와 분산 쿼리 실행: 쿼리 페더레이션의 원리

쿼리 페더레이션이 왜 필요한가 데이터 플랫폼이 커지면 데이터가 여러 곳에 흩어진다. 운영 DB는 MySQL과 PostgreSQL에, 분석 데이터는 S3의 Parquet 파일로, 이벤트 스트림은 Kafka에, 집계 결과는 ClickHouse에. 이 데이터를 함께 분석하려면 어떻게 해야 하는가. 전통적인 접근법은 ETL로 데이터를 한 곳에 모으는 것이다. 그런데 이 방법에는 한계가 있다. 원천 시스템이 바뀔 때마다 파이프라인을 수

경로human/study/content/query-federation/01-trino-architecture-distributed-execution.md
카테고리Study
태그#architecture #distributed #execution #federation #infra #mysql #study #trino

쿼리 페더레이션이 왜 필요한가

데이터 플랫폼이 커지면 데이터가 여러 곳에 흩어진다. 운영 DB는 MySQL과 PostgreSQL에, 분석 데이터는 S3의 Parquet 파일로, 이벤트 스트림은 Kafka에, 집계 결과는 ClickHouse에. 이 데이터를 함께 분석하려면 어떻게 해야 하는가.

전통적인 접근법은 ETL로 데이터를 한 곳에 모으는 것이다. 그런데 이 방법에는 한계가 있다. 원천 시스템이 바뀔 때마다 파이프라인을 수정해야 하고, 데이터 복사본이 늘어나면 신선도 문제와 비용 문제가 생긴다. 탐색적 분석을 위해 매번 ETL을 새로 짜는 것도 비효율이다.

쿼리 페더레이션(query federation)은 다른 방향으로 접근한다. 데이터를 옮기는 대신 쿼리를 데이터 위치로 보낸다. 하나의 SQL로 MySQL, S3, PostgreSQL에 동시에 질의하고 결과를 조인할 수 있다.

Trino(구 PrestoSQL)는 이 아이디어를 대규모로 구현한 분산 SQL 쿼리 엔진이다.


Trino의 역사

Trino의 역사는 Facebook의 Presto에서 시작한다.

2026년 기준 Trino는 활발하게 개발 중이며, Trino Software Foundation이 관리한다. 주요 버전은 400번대를 넘어섰다. Starburst(상용 배포판)와 AWS Athena v3가 Trino를 기반으로 한다.

prestodb(Meta의 Presto)와 Trino는 초기에는 코드 베이스를 공유했지만, 이후 서로 다른 방향으로 발전해 현재는 호환되지 않는 별도 제품이다.


아키텍처 개요

Trino는 코디네이터(Coordinator)와 워커(Worker) 역할로 나뉜다. 외부에서 보면 단일 SQL 엔드포인트처럼 보이지만, 내부에서는 수십~수백 대의 워커가 병렬로 실행한다.

클라이언트 JDBC/ODBC trino-cli SQL Coordinator Parser / Analyzer Logical Planner Cost-Based Optimizer Distributed Planner Scheduler / REST API Metadata / Statistics Worker 1 Task 실행 커넥터 I/O Exchange 처리 Worker 2 Task 실행 커넥터 I/O Exchange 처리 Worker N Task 실행 커넥터 I/O Exchange 처리 Task 배정 MySQL PostgreSQL S3 (Iceberg) Kafka ClickHouse 커넥터(Connector)로 각 데이터소스 접속
Trino 클러스터 아키텍처

코디네이터(Coordinator)

클러스터에서 단일 노드다. 클라이언트 요청을 받아 쿼리를 파싱하고, 최적화하고, 실행 계획을 워커에게 배분한다. 자신도 일부 연산(마지막 집계, 결과 반환)을 수행할 수 있지만, 대부분의 I/O 작업은 워커가 담당한다.

코디네이터는 단일 장애점(SPOF)이다. 고가용성이 필요하면 두 번째 코디네이터를 대기 상태로 두거나, Kubernetes에서 재시작을 자동화한다.

워커(Worker)

실제 데이터를 읽고, 필터링하고, 셔플하고, 집계한다. 커넥터를 통해 외부 데이터소스에 연결하고, 워커 간 데이터를 교환(exchange)한다. 코디네이터가 REST API로 지시를 내리면 워커가 실행하고 결과를 반환한다.

워커는 무상태(stateless)에 가깝다. Trino의 장애 허용성(fault tolerance) 기능을 켜면 태스크 실패 시 코디네이터가 다른 워커에서 재시작할 수 있다.


커넥터 프레임워크

Trino의 핵심 강점은 커넥터(Connector) 아키텍처다. 새로운 데이터소스를 지원하려면 커넥터를 구현해 플러그인으로 추가한다.

커넥터가 구현하는 인터페이스:

공식 지원 커넥터 목록(2025 기준 주요 항목):

커넥터데이터소스특이사항
HiveHDFS, S3, GCS 등의 ORC/Parquet/Avro가장 성숙, Metastore 필요
IcebergS3, HDFS 등의 Iceberg 테이블ACID, time travel 지원
Delta LakeS3, ADLS 등의 Delta 테이블Delta Lake 스펙 지원
HudiS3, HDFS 등의 Hudi 테이블CoW, MoR 모드
MySQLMySQL, MariaDBPushdown 지원
PostgreSQLPostgreSQLPushdown 지원
ClickHouseClickHouse집계 Pushdown 일부
KafkaKafka 토픽실시간 읽기
MongoDBMongoDB컬렉션
RedisRedis키-값
TPCH / TPCDS내장 벤치마크 데이터테스트용

Predicate Pushdown: Trino는 WHERE 조건을 커넥터로 "밀어내려" 데이터소스에서 필터를 적용하도록 한다. MySQL 커넥터가 pushdown을 지원하면 WHERE id = 42 조건을 MySQL이 처리하고 Trino는 해당 행만 받는다. Pushdown이 없으면 전체 테이블을 Trino가 읽어서 직접 필터링한다.


쿼리 생명주기

SQL이 Trino에 제출되면 다음 단계를 거친다.

SQL 텍스트
파싱
AST 생성
분석
이름 해결·타입 검사
논리 플랜
관계 대수 트리
CBO 최적화
조인 순서·푸시다운
분산 실행 계획
Stage·Task 분해
분산 실행 단계
Split 목록 생성
SplitManager
워커 배정
Scheduler
데이터 읽기
PageSource (커넥터)
Exchange
셔플·파티셔닝
집계·결합
Operator 파이프라인
결과 반환
SQL 쿼리 실행 파이프라인

Stage, Task, Split

분산 실행 계획의 핵심 단위다.

예시: SELECT a.name, b.amount FROM mysql.customers a JOIN s3.orders b ON a.id = b.cid

Stage 0 (Final): 결과 정렬·반환 (Coordinator에서 실행)
Stage 1 (조인): Hash Join 수행 (여러 Worker에서 병렬)
  ├─ Task 1-1 (Worker 1)
  ├─ Task 1-2 (Worker 2)
  └─ Task 1-3 (Worker 3)
Stage 2 (MySQL 스캔): customers 테이블 스캔
  ├─ Split: id BETWEEN 1 AND 10000 → Task 2-1 (Worker 1)
  └─ Split: id BETWEEN 10001 AND 20000 → Task 2-2 (Worker 2)
Stage 3 (S3 스캔): orders Parquet 파일 읽기
  ├─ Split: s3://bucket/orders/part-0001.parquet → Task 3-1 (Worker 1)
  ├─ Split: s3://bucket/orders/part-0002.parquet → Task 3-2 (Worker 2)
  └─ Split: s3://bucket/orders/part-0003.parquet → Task 3-3 (Worker 3)

비용 기반 최적화(CBO)

Trino의 CBO는 테이블 통계(행 수, 컬럼 카디널리티, null 비율, 히스토그램)를 사용해 최적 조인 순서와 조인 알고리즘을 결정한다.

CBO가 잘 동작하려면 테이블 통계가 최신이어야 한다. Hive/Iceberg 커넥터에서 ANALYZE table_name을 주기적으로 실행해 통계를 갱신한다.


메모리 관리와 스필(Spill)

Trino는 기본적으로 메모리 내에서 연산한다. 워커 JVM 힙의 상당 부분이 쿼리 실행에 사용된다.

메모리 풀 구분

각 워커는 메모리를 두 풀로 나눈다.

query.max-memory는 클러스터 전체에서 한 쿼리가 사용할 수 있는 User Memory 총합이다. 이를 초과하면 Query exceeded distributed user memory limit으로 쿼리가 실패한다.

스필(Spill to Disk)

메모리 제한을 초과할 것으로 예상되면 중간 결과를 디스크로 내린다. 이를 활성화하려면:

spill-enabled=true
spill-path=/mnt/fast-disk/trino-spill
max-spill-per-node=200GB

스필은 성능 저하를 동반하지만 쿼리 실패보다는 낫다. SSD가 있는 경로를 스필 디렉터리로 설정해야 성능 영향을 최소화할 수 있다.


장애 허용(Fault Tolerance)

초기 Trino는 하나의 워커 태스크가 실패하면 전체 쿼리가 재시작되었다. 수십 분짜리 쿼리가 98% 완료 후 실패하면 처음부터 다시 실행해야 했다.

Exchange Materialization(ETM)이 이를 해결한다. Exchange(워커 간 데이터 전달) 결과를 중간 스토리지(보통 S3 또는 HDFS)에 저장한다. 특정 워커가 실패하면 그 태스크만 다른 워커에서 재시작하고 저장된 Exchange 결과를 재사용한다.

retry-policy=TASK로 설정하면 태스크 단위 재시도가 활성화된다.

retry-policy=TASK
task-retry-attempts-per-task=3
exchange.deduplication-buffer-size=32MB

ETM은 스토리지 쓰기·읽기 오버헤드가 있어 짧은 쿼리보다 긴 쿼리에서 이득이 크다.


보안

Trino의 보안은 인증(Authentication)과 인가(Authorization)로 나뉜다.

인증

TLS는 필수 설정이다(http-server.https.enabled=true). 클라이언트↔코디네이터, 코디네이터↔워커 구간 모두 TLS를 권장한다.

인가

Row-level security는 뷰(View)를 통해 구현한다. 사용자나 그룹에 따라 다른 조건이 적용된 뷰를 만들고, 기본 테이블에 직접 접근을 차단한다.


Trino vs Spark SQL vs DuckDB

쿼리 엔진 선택은 워크로드 특성에 따라 달라진다.

항목TrinoSpark SQLDuckDB
실행 모델분산 MPP, 메모리 내 파이프라인DAG 기반 배치, 스테이지 디스크 기록단일 프로세스, 벡터화 실행
적합 워크로드인터랙티브 쿼리 (<1분), 페더레이션대규모 배치, ETL, ML 전처리단일 노드 OLAP, 로컬 파일 분석
메모리 사용크다 (중간 결과 메모리)중간 (스필 기본)작다 (효율적 컬럼형)
지연 (latency)낮음 (초~분)중간 (분)매우 낮음 (밀리초)
클러스터필요 (수십~수백 노드)필요불필요 (단일 서버)
페더레이션강점 (커넥터 생태계)제한적제한적 (DeltaLake, Iceberg 지원)
운영 복잡도중간높음없음

현실적인 선택 기준:


운영 핵심 지표

지표확인 방법의미
쿼리 큐 대기 시간system.runtime.queries실행 슬롯이 부족하면 증가
워커 메모리 사용JMX trino:type=Memory,*OOM 임박 신호
Split 처리 속도UI의 Rows/sec커넥터 I/O 병목
GC 시간JVM GC 로그힙 과부하 징후
CPU 사용률워커별 CPU연산 균등 분배 여부

Trino는 웹 UI(http://coordinator:8080)에서 실행 중인 쿼리, Stage별 진행 상황, 워커별 리소스를 실시간으로 볼 수 있다.


실무 체크리스트: Trino 운영

  1. 코디네이터 고가용성: 단일 코디네이터라면 k8s Deployment로 자동 재시작을 보장한다.
  2. 워커 JVM 힙 설정: jvm.config에서 -Xmx를 서버 RAM의 70~80%로. GC 알고리즘은 G1GC 권장.
  3. 커넥터 별 통계 갱신: Iceberg/Hive는 ANALYZE, MySQL/PostgreSQL은 자동 통계.
  4. 스필 경로: SSD 경로를 spill-path에 지정. 스필이 과도하면 메모리 증설 고려.
  5. CBO 활성화: optimizer.join-reordering-strategy=AUTOMATIC (기본값 확인).
  6. Fault Tolerance: 30분 이상 쿼리라면 retry-policy=TASK와 ETM 스토리지 설정.
  7. 쿼리 제한: query.max-execution-time, query.max-memory로 폭주 쿼리 제한.
  8. TLS 필수: 코디네이터 HTTPS, 내부 노드 간 TLS도 프로덕션에서 켠다.

References