운영 대시보드와 장애 대응: 메모리, GC, 스필, 재시도
운영자가 Trino에서 가장 먼저 마주치는 문제
Trino는 메모리 집약적인 엔진이다. 분산 조인, 집계, 셔플(Exchange) 과정에서 워커 노드가 수십~수백 GB 데이터를 메모리에 들고 처리한다. 운영 중에 가장 자주 발생하는 장애는 다음 세 가지다.
- OOM (Out of Memory): 쿼리가 워커 힙을 초과해 워커가 죽거나 쿼리가 강제 종료된다.
- 느린 GC: Java 힙이 과도하게 활용되면 G1GC 정지 시간이 길어지고 쿼리 처리 지연이 생긴다.
- 스필(Spill): 메모리 부족 시 임시 디스크를 사용하는데, 디스크 I/O가 느리면 쿼리 시간이 수십 배 늘어난다.
세 가지 모두 메모리 설정이 핵심이다.
JVM 메모리 구성
Trino 노드는 etc/jvm.config로 JVM을 설정한다. 코디네이터와 워커의 역할이 다르므로 힙 크기와 GC 목표도 달리 잡는 것이 좋다.
워커 etc/jvm.config 예시
-server
-Xmx54G
-XX:InitialRAMPercentage=80
-XX:MaxRAMPercentage=80
-XX:G1HeapRegionSize=32M
-XX:+ExplicitGCInvokesConcurrent
-XX:+ExitOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError
-XX:MaxGCPauseMillis=500
-XX:ParallelGCThreads=48
-XX:ConcGCThreads=12
-XX:-OmitStackTraceInFastThrow
-XX:ReservedCodeCacheSize=512M
-XX:PerMethodRecompilationCutoff=10000
-XX:PerBytecodeRecompilationCutoff=10000
-Djdk.attach.allowAttachSelf=true
-Djdk.nio.maxCachedBufferSize=2000000코디네이터 etc/jvm.config 예시
코디네이터는 쿼리 계획, 스케줄링, 메타데이터 처리가 주 역할이므로 워커보다 힙이 작아도 되고, GC 정지 목표를 더 짧게 잡는다.
-server
-Xmx16G
-XX:MaxGCPauseMillis=150
-XX:InitialHeapOccupancyPercent=35
-XX:+ExitOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError
-XX:ReservedCodeCacheSize=512Metc/config.properties 메모리 관련 설정
# 워커 한 대에서 단일 쿼리가 쓸 수 있는 최대 메모리
query.max-memory-per-node=38GB
# JIT·GC·스레드 스택을 위한 힙 예비 공간
memory.heap-headroom-per-node=12GB
# 클러스터 전체 쿼리 메모리 총합 한도
query.max-memory=200GB
# 쿼리가 메모리를 초과했을 때 강제 종료까지 대기 시간
query.low-memory-killer.delay=5mquery.max-memory-per-node + memory.heap-headroom-per-node의 합이 -Xmx 값을 초과하면 Trino가 시작을 거부한다.
스필(Spill to Disk)
스필은 쿼리 메모리 한도를 초과할 때 임시 데이터를 디스크에 기록하는 레거시 메커니즘이다. 최신 운영 환경에서는 FTE(Fault-Tolerant Execution)를 우선 고려하지만, 스필만 단독으로 활성화하는 경우도 여전히 있다.
# etc/config.properties
spill-enabled=true
spill-path=/data1/trino-spill,/data2/trino-spill
max-spill-per-node=100GB스필 경로는 빠른 로컬 NVMe에 두는 것이 좋다. S3나 NFS 위에서 스필하면 쿼리가 수십 배 느려진다.
주의: 스필이 자주 발생하고 있다면 메모리 부족 신호다. 스필을 활성화하는 것보다 FTE 도입, 쿼리 메모리 한도 조정, 또는 워커 메모리 증설을 먼저 검토한다.
Fault-Tolerant Execution (FTE)과 재시도
FTE가 해결하는 문제
기본 Trino 모드에서는 워커 한 대가 죽으면 해당 워커가 처리 중이던 쿼리 전체가 실패한다. 분석용 대형 쿼리일수록 재시도 비용이 크다.
FTE는 쿼리 중간 결과를 스풀(Spool) 스토리지(S3 등)에 저장해, 워커 장애 시 다른 워커가 이어받을 수 있게 한다.
(retry-policy=NONE) 워커 1개 실패 ↓ 쿼리 전체 실패
클라이언트에 오류 반환
(retry-policy=QUERY) 워커 장애 감지 ↓ 쿼리 처음부터
재실행 작은 쿼리에 적합
(retry-policy=TASK) 워커 장애 감지 ↓ 실패 Task만
다른 워커에서 재실행 중간 결과 S3에 저장 대형 배치 쿼리에 권장
FTE 설정
etc/config.properties
retry-policy=TASKExchange Manager 설정 (S3 기반)
etc/exchange-manager.properties:
exchange.base-directories=s3://my-bucket/trino-exchange
exchange-manager.name=filesystemS3 외에 HDFS나 로컬 파일시스템도 지원한다. 단, 로컬 파일시스템은 워커가 죽으면 데이터도 같이 사라지므로 재시도 효과가 없다.
TASK 재시도 시 메모리 재조정
특정 Task가 메모리 초과로 실패하면, Trino는 해당 Task를 더 큰 메모리 가진 워커에서 재시도하거나 노드 전체 메모리를 배정해 재실행한다. 이를 통해 일시적인 메모리 부족으로 인한 실패를 자동으로 흡수할 수 있다.
모니터링: 핵심 지표
Trino는 JMX와 OpenMetrics(Prometheus 호환) 두 가지 메트릭 수집 방식을 제공한다.
OpenMetrics 엔드포인트 활성화
# etc/config.properties
http-server.prometheus.enabled=truehttp://coordinator:8080/metrics에서 Prometheus 형식의 메트릭을 가져올 수 있다.
주요 모니터링 지표
JMX 커넥터로 SQL 조회
Trino의 JMX 커넥터를 사용하면 SQL로 내부 상태를 쿼리할 수 있다.
-- 현재 실행 중인 쿼리 수
SELECT "runningqueries" FROM jmx.current."io.trino.execution:name=querymanager";
-- 워커별 힙 사용 현황
SELECT node_id,
round(used / 1e9, 1) AS heap_used_gb,
round(max / 1e9, 1) AS heap_max_gb,
round(used * 100.0 / max, 1) AS heap_pct
FROM jmx.current."java.lang:name=heap memory usage,type=memoryusage"
JOIN system.runtime.nodes USING (node_id);
-- OOM으로 강제 종료된 쿼리 수
SELECT "killedqueries" FROM jmx.current."io.trino.execution:name=querymanager";장애 대응 패턴
패턴 1: 워커 OOM → 클러스터 불안정
증상: 워커가 갑자기 재시작, 클러스터의 전체 쿼리 실패율 급등.
진단 순서:
- 코디네이터 Web UI(
http://coordinator:8080) → Finished Queries 탭에서 최근 실패 쿼리 확인. - 실패 쿼리의 Query Detail →
KILLED또는FAILED사유 확인. - 워커 로그(
/var/log/trino/server.log)에서OutOfMemoryError발생 시각 확인. - 쿼리 ID로
EXPLAIN ANALYZE재실행해 메모리 사용량 추정.
완화 조치:
-- 특정 쿼리 강제 종료
SELECT * FROM system.runtime.queries WHERE state = 'RUNNING';
CALL system.runtime.kill_query(query_id => 'xxxxxxxxxx', message => 'OOM 방지');재발 방지:
query.max-memory-per-node값을 쿼리 분석 후 조정한다.- 리소스 그룹(Resource Groups)으로 사용자별·태그별 메모리 한도를 설정한다.
패턴 2: GC 정지로 인한 쿼리 지연
증상: 일부 쿼리만 주기적으로 느려지고, 워커 로그에 GC 경고.
진단:
# 워커 GC 로그 분석 (-Xlog:gc* 설정 필요)
grep "pause" /var/log/trino/gc.log | tail -20
# JMX로 GC 시간 조회
SELECT "collectiontime" / 1000.0 AS gc_seconds
FROM jmx.current."java.lang:name=g1 old generation,type=garbagecollector";완화 조치:
- GC 정지가 500ms를 자주 초과하면
-Xmx를 늘리거나 워커 수를 늘려 노드당 부하를 줄인다. -XX:G1HeapRegionSize값이 힙 크기에 비해 너무 작으면 리전이 너무 많아져 GC 오버헤드가 커진다. 힙이 30GB 이상이면 32M으로 설정한다.
패턴 3: 스필 디스크 포화
증상: 스필 경로의 디스크 사용률이 90%를 초과, 스필 쓰기 실패로 쿼리 실패.
진단:
df -h /data1/trino-spill /data2/trino-spill
ls -lh /data1/trino-spill/ # 스필 파일 크기 확인완화 조치:
- 실행 중인 큰 쿼리를
kill_query로 종료해 스필 파일을 해제한다. max-spill-per-node한도를 낮춰 스필 총량을 제한한다.
패턴 4: 코디네이터 응답 없음
증상: Web UI 접속 불가, 모든 새 쿼리 대기 상태.
진단: 코디네이터 힙 덤프를 분석하거나 JVM 스레드 덤프를 확인한다.
kill -3 <coordinator-pid> # 스레드 덤프 출력 (server.log에 기록)코디네이터는 워커보다 힙이 작아도 되지만, 동시 쿼리 수가 많을 때 스케줄링 오버헤드가 쌓인다. 코디네이터 노드를 쿼리 실행에서 제외(node-scheduler.include-coordinator=false)하면 안정성이 높아진다.
리소스 그룹으로 공정한 자원 분배
메모리 관련 장애의 상당수는 특정 사용자나 워크로드가 메모리를 독점해서 발생한다. 리소스 그룹으로 쿼리별 한도를 강제할 수 있다.
{
"rootGroups": [
{
"name": "global",
"softMemoryLimit": "80%",
"hardConcurrencyLimit": 100,
"maxQueued": 1000,
"subGroups": [
{
"name": "adhoc",
"softMemoryLimit": "30%",
"hardConcurrencyLimit": 20,
"maxQueued": 200,
"schedulingPolicy": "fair"
},
{
"name": "batch",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 10,
"maxQueued": 100,
"schedulingPolicy": "weighted_fair"
}
]
}
],
"selectors": [
{
"user": "airflow",
"group": "batch"
},
{
"group": "adhoc"
}
]
}softMemoryLimit은 이 그룹이 사용할 수 있는 전체 메모리의 비율이다. 이 한도를 초과하면 새 쿼리가 큐에 쌓이고 실행이 지연된다.
운영 대시보드 구성 권고
| 패널 | 지표 | 경고 임계값 |
|---|---|---|
| 실행 중 쿼리 수 | runningqueries | >50 (클러스터 규모에 따라 조정) |
| 큐 대기 쿼리 수 | queuedqueries | >20 지속 시 용량 부족 |
| 클러스터 메모리 사용률 | 예약 메모리 / 전체 | >80% |
| 워커 힙 사용률 (P95) | 워커별 JVM 힙 | >85% |
| GC 정지 시간 (P99) | collectiontime | >500ms per cycle |
| OOM 종료 쿼리 | killedqueries rate | >0 |
| 스필 발생률 | 스필 쓰기 바이트 | 지속적으로 증가 시 경고 |
| 워커 재시작 이벤트 | 노드 수 변화 | 갑작스러운 감소 |
체크리스트
query.max-memory-per-node + memory.heap-headroom-per-node < -Xmx인지 확인한다.node-scheduler.include-coordinator=false로 코디네이터를 워커 역할에서 제외한다.- OOM 발생 즉시 힙 덤프가 남도록
-XX:+HeapDumpOnOutOfMemoryError를 설정한다. - 대형 배치 쿼리가 있는 클러스터에는 FTE(retry-policy=TASK) + S3 Exchange Manager를 활성화한다.
- 스필 경로는 빠른 로컬 스토리지에, 모니터링 경보와 함께 운영한다.
- 리소스 그룹으로 ad-hoc과 배치 쿼리를 분리해 상호 영향을 최소화한다.
- Prometheus + Grafana로 핵심 메트릭 대시보드를 구성하고, OOM·워커 재시작 경보를 설정한다.
References
- Trino Spill to Disk 공식 문서, https://trino.io/docs/current/admin/spill.html
- Trino Fault-tolerant Execution 공식 문서, https://trino.io/docs/current/admin/fault-tolerant-execution.html
- Trino Resource Management Properties, https://trino.io/docs/current/admin/properties-resource-management.html
- Trino Monitoring with JMX, https://trino.io/docs/current/admin/jmx.html
- Trino OpenMetrics 공식 문서, https://trino.io/docs/current/admin/openmetrics.html
- Trino Deploying (JVM config), https://trino.io/docs/current/installation/deployment.html
- Starburst: Fault-tolerant Execution, https://docs.starburst.io/latest/admin/fault-tolerant-execution.html
- Vivek Jain: Right-Sizing Trino — A Data-Driven Guide to Cluster Memory Tuning, https://medium.com/@vjain143/right-sizing-trino-a-data-driven-guide-to-cluster-memory-tuning-a31b6a80c2f6
- GitHub: Trino JVM OOM Discussion, https://github.com/trinodb/trino/discussions/27446
- nil1729: Trino JMX Monitoring with Prometheus and Grafana, https://nil1729.github.io/trino-jmx-monitoring/