스테이지 레벨 스풀링

스테이지 레벨 스풀링 (Stage-Level Spooling)

공통 표현식 재사용이라고도 불러요.

출처: 문서

본문

⚠️ 스테이지 레벨 스풀링은 아직 개발 중이며 일부 제한이 있을 수 있는 multi-stage 엔진의 선택적 최적화예요. 이 "개발 중" 표시는 스풀링 자체에만 적용되며, 일반적으로 사용 가능한(GA) 핵심 multi-stage 엔진에는 적용되지 않아요. 기본적으로 스풀링을 켜는 것은 권장하지 않고, 실제로 쿼리 성능을 개선하는지 테스트한 후 쿼리 단위로 활성화하는 걸 권장해요. 문제가 생기면 적극적으로 보고해 주세요.

개요 (Overview)

multi-stage 쿼리 엔진에서는 쿼리가 실수로 같은 테이블을 읽거나 같은 조인을 여러 번 실행하는 경우가 흔해요. 이는 예를 들어 WITH 표현식이나 복잡한 조인을 사용할 때 발생할 수 있어요. 이런 중복 연산은 특히 대규모 데이터셋이나 조인·집계 같은 비싼 연산을 다룰 때 상당한 성능 오버헤드를 유발할 수 있어요.

이 문제를 해결하기 위해 Apache Pinot는 이제 스테이지 레벨 스풀링(stage-level spooling) 을 지원해요. 이 기능은 쿼리 실행 계획에서 중복 스테이지를 식별하고 제거해요. 이 최적화는 동등한 스테이지가 단 한 번만 실행되도록 보장하여 불필요한 계산을 줄이고, 특히 반복되는 테이블 스캔·조인·집계가 포함된 스테이지에서 쿼리 성능을 개선해요.

FAQ

특정 쿼리에 대해 이 기능을 어떻게 켜고 끄나요?

쿼리에서 SET useSpools = true; 또는 SET useSpools = false;를 사용해요.

두 스테이지가 동등하지 않으면 어떻게 되나요?

쿼리는 최적화 없이 평소대로 실행돼요.

내 쿼리에서 스테이지 레벨 스풀링이 동작하는지 어떻게 확인하나요?

스테이지 통계 시각화 도구나 EXPLAIN IMPLEMENTATION PLAN FOR를 사용해 쿼리 실행 계획을 확인해요. 각 send 연산자의 스테이지 ID를 보세요. 스테이지 레벨 스풀링이 적용되면 동등한 스테이지에 대해 같은 스테이지 ID가 보여야 해요.

이 기능은 WITH 표현식에만 제한되나요?

아니요. 작성 방식과 무관하게 동등한 스테이지가 있는 모든 쿼리에 동작해요.

쿼리에서 WITH 표현식을 두 번 사용하면 스테이지 레벨 스풀링이 항상 적용되나요?

아니요. 이 기능은 다른 최적화가 적용된 후 스테이지가 동등한 경우에만 적용돼요. 자세한 내용은 제한 사항 섹션을 참조하세요.

구성 (Configuration)

스테이지 레벨 스풀링은 기본적으로 비활성화되어 있으며 다음 방식으로 활성화할 수 있어요.

  • 전역 설정: 브로커 구성 파일에서 pinot.broker.multistage.spools를 변경해 모든 쿼리에 대해 기본적으로 스테이지 레벨 스풀링을 활성화할지 설정해요.
  • 쿼리별 설정: 쿼리에서 useSpools 옵션을 사용해 해당 쿼리에 대해 스테이지 레벨 스풀링을 켜거나 꺼요.

예시 (Example)

스테이지 레벨 스풀링이 어떻게 동작하는지 다음 예시를 통해 이해해 보세요.

SET useSpools = false; -- disable stage-level spooling
SELECT *
FROM T1
JOIN T2 as t2first
    ON T1.col1 = t2first.col2
JOIN T2 as t2second
    ON t2first.col3 = t2second.col3

이 쿼리는 다음 계획을 생성해요.

flowchart BT
J2([JOIN 2])
J1([JOIN 1])
S1([Scan T1])
S2([Scan T2])
S22([Scan T2])

J2 --> J1
S1 --> J2
S2 --> J2
S22 --> J1

스테이지 레벨 스풀링이 활성화되면(SET useSpools = true 사용), 계획은 다음과 같이 최적화돼요.

flowchart BT
J2([JOIN 2])
J1([JOIN 1])
S1([Scan T1])
S2([Scan T2])

J2 --> J1
S1 --> J2
S2 --> J2
S2 --> J1

제한 사항 (Limitations)

동등한 스테이지 (Equivalent stages)

스테이지 레벨 스풀링은 쿼리 플래너가 동등한 스테이지를 감지하면 자동으로 적용돼요. 사용자는 이 최적화를 활용하기 위해 쿼리를 수정할 필요가 없어요. 다만 동작 방식을 이해하면 더 효율적인 쿼리를 작성하는 데 도움이 돼요.

두 스테이지는 다음 조건을 만족하면 동등하다고 간주돼요.

  • 동일한 연산자를 가짐
    • 즉, 같은 컬럼을 투영하고, 같은 필터를 적용하고, 같은 집계를 수행해야 해요.
  • 자식(children) 스테이지가 동등함
  • 서로 다른 부모(parent)를 가짐
    • 같은 조인 또는 유니온의 직접적인 자식인 두 스테이지는 동등하지 않아요.

이 조건들은 대부분의 Pinot 논리 최적화가 완료된 후에 적용돼요. 즉, SQL 문장에서 두 스테이지가 동등해도 최종 계획에서는 동등하지 않을 수 있어요. 이는 최적화가 적용되기 전에 주 쿼리로 확장되는 단순 문법 설탕인 WITH 표현식을 사용할 때 특히 흔해요.

두 가지 Pinot 최적화가 스테이지 레벨 스풀링을 쉽게 비활성화할 수 있어요: Filter와 Projection pushdown.

Filter pushdown은 필터를 실행 계획 더 깊은 곳으로 밀어 넣는 최적화예요. 예를 들어 다음 쿼리를 생각해 보세요.

WITH with1 AS (
  SELECT col1, col2, col3, count(*) 
  FROM table1 
  GROUP BY col1, col2, col3
)
SELECT * FROM with1 as t2 
JOIN table2 as t2
ON t1.col2 = t2.col2
JOIN with1 as t3
ON t2.col3 = t3.col3
WHERE t1.col3 = 2

이 쿼리에서 필터 t1.col3 = 2는 두 조인 다음에 정의되지만, 그 조건자가 t1에만 의존하므로 Pinot는 필터를 밀어 넣고 계획이 다음과 같이 됩니다.

SELECT * FROM (
  SELECT col1, col2, 2, count(*) 
  FROM table1 
  WHERE col3 = 2
  GROUP BY col1, col2
) as t2 
JOIN table2 as t2
ON t1.col2 = t2.col2
JOIN (
  SELECT col1, col2, col3, count(*)
  FROM table1
  GROUP BY col1, col2
) as t3
ON t2.col3 = t3.col3

보시다시피 쿼리에서 WITH 표현식의 두 사용이 모두 확장되었고, WHERE 필터가 첫 번째 것으로 하향 푸시되었어요. 이로 인해 두 서브쿼리가 달라지므로 스테이지 레벨 스풀링이 적용되지 않아요.

Projection pushdown에서도 같은 일이 발생해요. 이 최적화는 프로젝션을 실행 계획 아래로 밀어 넣어요. 즉, 같은 쿼리에서 WITH 표현식을 두 번 사용하지만 매번 다른 컬럼을 선택하면 스테이지가 동등하지 않게 돼요.

Pinot는 여러 이유로 이 최적화들에 스테이지 레벨 스풀링보다 높은 우선순위를 주기로 결정했어요. 주요 이유는 이 최적화들이 더 흔하고 쿼리 성능에 더 큰 영향을 주기 때문이에요. 스테이지 레벨 스풀링이 더 높은 우선순위를 가져야 한다고 생각하는 사용 사례가 있다면 GitHub 이슈로 보고해 주세요.

알려진 문제 (Known issues)

스테이지 레벨 스풀링은 매우 광범위한 기능이라 일부 시나리오를 예측하기 어려워요. 그래서 아직 개발 중으로 간주되고 기본적으로 활성화되지 않는 것이에요. 모든 쿼리에 활성화하기 전에 테스트하고, 문제를 발견하면 GitHub 이슈를 열어 주세요.

다음은 이 기능의 설계 및 초기 개발 중에 발견된 알려진 문제 목록이에요.

블록, 타임아웃, 메모리 (Blocks, timeouts and memory)

어떤 상황에서는 스풀된 스테이지에 데이터를 소비하는 데 시간이 걸리는 부모가 있을 수 있어요. 그 시간 동안 스풀된 스테이지는 데이터를 버퍼링해야 하므로 메모리 압력, 타임아웃, 또는 다른 오류가 발생할 수 있어요.

중간 스테이지에 대한 제한된 지원 (Limited support on intermediate stages)

스테이지 레벨 스풀링의 초기 사용자들은 매우 큰 쿼리에서 multi-stage 스풀링을 활성화했을 때 일부 문제를 발견했어요. 때로 중간 스테이지가 스풀되지 않아 계획 시간 오류가 발생했어요.

버전 지원 (Version support)

  • 이 기능은 Apache Pinot 버전 1.3.0 이상에서 사용할 수 있어요.
  • 중간 스테이지를 스풀할 수 있는 첫 번째 버전은 1.4.0이에요.
  • 스테이지 통계 시각화 도구는 Apache Pinot 1.3.0에서 도입되었어요.
    • 1.3.0에서는 각 스풀이 같은 스테이지 ID를 가진 서로 다른 노드로 표시되므로 시각화가 통계를 저장하는 JSON과 같은 모양이 돼요.
    • 1.4.0에서는 스풀된 스테이지가 단일 노드로 표시되고, 그 스테이지에서 읽는 스테이지로의 간선(edge)이 표시돼요.

참고 자료 (References)

  • GitHub Issue #14196: 이 기능을 작성하기 위한 원래 설계와 작업을 추적하는 GitHub 이슈예요.
  • Design Document: 조금 오래됐지만 이 기능이 내부적으로 어떻게 동작하는지 이해하는 데 유용해요.

더 알아보기 (Learn more)