LLM WikiAccess-protected knowledge portal
← 스터디 홈
5편 · 약 25분

운영 대시보드와 장애 대응: 메모리, GC, 스필, 재시도

운영자가 Trino에서 가장 먼저 마주치는 문제

Trino는 메모리 집약적인 엔진이다. 분산 조인, 집계, 셔플(Exchange) 과정에서 워커 노드가 수십~수백 GB 데이터를 메모리에 들고 처리한다. 운영 중에 가장 자주 발생하는 장애는 다음 세 가지다.

  1. OOM (Out of Memory): 쿼리가 워커 힙을 초과해 워커가 죽거나 쿼리가 강제 종료된다.
  2. 느린 GC: Java 힙이 과도하게 활용되면 G1GC 정지 시간이 길어지고 쿼리 처리 지연이 생긴다.
  3. 스필(Spill): 메모리 부족 시 임시 디스크를 사용하는데, 디스크 I/O가 느리면 쿼리 시간이 수십 배 늘어난다.

세 가지 모두 메모리 설정이 핵심이다.


JVM 메모리 구성

Trino 노드는 etc/jvm.config로 JVM을 설정한다. 코디네이터와 워커의 역할이 다르므로 힙 크기와 GC 목표도 달리 잡는 것이 좋다.

워커 노드 메모리 구성 (예: 64 GB RAM) 물리 메모리 64 GB OS + 기타 프로세스 ≈ 10% 확보 JVM Heap (-Xmx54G) 쿼리 메모리 풀 query.max-memory-per-node = 38G 각 쿼리가 이 한도 내에서 메모리 예약 초과 시 → 스필 시도 or 쿼리 종료 힙 헤드룸 memory.heap-headroom-per-node = 12G JIT 컴파일, GC 오버헤드, 스레드 스택 = 총 힙 - query.max-memory-per-node JVM 비힙 Code Cache ReservedCodeCache Size=512M Metaspace Thread Stacks Direct Buffers 총 ≈ 2~4 GB 필수 제약: query.max-memory-per-node + memory.heap-headroom-per-node < Xmx
Trino 노드 메모리 레이어

워커 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=512M

etc/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=5m

query.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개 실패 쿼리 전체 실패
클라이언트에 오류 반환
QUERY 재시도
(retry-policy=QUERY)
워커 장애 감지 쿼리 처음부터
재실행
작은 쿼리에 적합
TASK 재시도
(retry-policy=TASK)
워커 장애 감지 실패 Task만
다른 워커에서 재실행
중간 결과 S3에 저장 대형 배치 쿼리에 권장
FTE 재시도 메커니즘

FTE 설정

etc/config.properties

retry-policy=TASK

Exchange Manager 설정 (S3 기반)

etc/exchange-manager.properties:

exchange.base-directories=s3://my-bucket/trino-exchange
exchange-manager.name=filesystem

S3 외에 HDFS나 로컬 파일시스템도 지원한다. 단, 로컬 파일시스템은 워커가 죽으면 데이터도 같이 사라지므로 재시도 효과가 없다.

TASK 재시도 시 메모리 재조정

특정 Task가 메모리 초과로 실패하면, Trino는 해당 Task를 더 큰 메모리 가진 워커에서 재시도하거나 노드 전체 메모리를 배정해 재실행한다. 이를 통해 일시적인 메모리 부족으로 인한 실패를 자동으로 흡수할 수 있다.


모니터링: 핵심 지표

Trino는 JMX와 OpenMetrics(Prometheus 호환) 두 가지 메트릭 수집 방식을 제공한다.

OpenMetrics 엔드포인트 활성화

# etc/config.properties
http-server.prometheus.enabled=true

http://coordinator:8080/metrics에서 Prometheus 형식의 메트릭을 가져올 수 있다.

주요 모니터링 지표

쿼리 처리량 실행 중 쿼리 수 큐 대기 쿼리 수 실패율 (1m/5m) 쿼리 완료 시간 P50/P99
메모리 사용 클러스터 총 예약 메모리 OOM으로 죽은 쿼리 수 워커별 힙 사용률 스필 발생 횟수/용량
JVM / GC 힙 사용률 (%) GC 정지 횟수·시간 Old Gen 증가 추세 Code Cache 사용률
클러스터 상태 워커 활성 수 워커 실패·재시작 이벤트 Exchange buffer 포화 Split 처리 속도
핵심 지표 카테고리

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 → 클러스터 불안정

증상: 워커가 갑자기 재시작, 클러스터의 전체 쿼리 실패율 급등.

진단 순서:

  1. 코디네이터 Web UI(http://coordinator:8080) → Finished Queries 탭에서 최근 실패 쿼리 확인.
  2. 실패 쿼리의 Query DetailKILLED 또는 FAILED 사유 확인.
  3. 워커 로그(/var/log/trino/server.log)에서 OutOfMemoryError 발생 시각 확인.
  4. 쿼리 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
스필 발생률스필 쓰기 바이트지속적으로 증가 시 경고
워커 재시작 이벤트노드 수 변화갑작스러운 감소

체크리스트

  1. query.max-memory-per-node + memory.heap-headroom-per-node < -Xmx인지 확인한다.
  2. node-scheduler.include-coordinator=false로 코디네이터를 워커 역할에서 제외한다.
  3. OOM 발생 즉시 힙 덤프가 남도록 -XX:+HeapDumpOnOutOfMemoryError를 설정한다.
  4. 대형 배치 쿼리가 있는 클러스터에는 FTE(retry-policy=TASK) + S3 Exchange Manager를 활성화한다.
  5. 스필 경로는 빠른 로컬 스토리지에, 모니터링 경보와 함께 운영한다.
  6. 리소스 그룹으로 ad-hoc과 배치 쿼리를 분리해 상호 영향을 최소화한다.
  7. 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/