멀티-스테이지 라이트 모드

멀티-스테이지 라이트 모드 (Multistage Lite Mode)

Multistage Engine Lite Mode를 소개해요.

참고: MSE Lite Mode는 Pinot 1.4에서 도입되었고 Pinot 1.5.0부터 안정화되었어요.

출처: 문서

본문

Multistage Engine (MSE) Lite Mode는 셀프-서비스 및 고-QPS 테넌트를 위한 선택적이고 guardrail 지향적인 실행 모드예요. 추가적인 제한 없이는 쿼리가 많은 수의 레코드를 스캔하거나 비용이 큰 연산을 실행할 수 있는데, 이는 공유 테넌트의 안정성에 영향을 주고 새 사용 사례 온보딩에 마찰을 만들 수 있어요. Lite Mode는 각 잎 스테이지에서 반환되는 행을 상한으로 제한하고 리소스 경계를 자동으로 더 엄격하게 적용해 이를 해결해요.

이는 대부분의 사용자가 Window Functions, Subqueries 같은 고급 SQL 기능이 필요하지만 많은 데이터를 스캔하거나 완전히 분산된 조인을 실행하는 데는 관심이 없다는 관찰에 기반해요.

개요

MSE Lite Mode의 주요 특징은 다음과 같아요.

  • 사용자는 여전히 Window Functions, Subqueries, Joins 등 모든 MSE 쿼리 기능을 사용할 수 있어요.
  • 그러나 잎 스테이지가 반환하는 최대 행 수는 사용자가 구성할 수 있는 값으로 설정돼요. 기본값은 100,000이에요.
  • 쿼리 실행은 Single-stage Engine과 유사한 scatter-gather 패러다임을 따라요. 이는 Pinot Servers 간에 셔플이 사용되는 일반 MSE와는 달라요.
  • 잎 스테이지(들)는 Servers에서 실행되고, 그 외 모든 연산자는 Broker에서 단일 스레드를 사용해 실행돼요.

Multistage Engine 쿼리에서 잎 스테이지는 보통 Table Scan, 선택적 Project, 선택적 Filter, 선택적 Aggregate Plan Node를 말해요.

현재 MSE Lite Mode의 모든 조인은 Broker에서 실행돼요. Colocated Joins는 이론적으로 Servers에서 실행될 수 있으므로 다음 릴리스에서 바뀔 수 있어요.

예시

MSE Lite Mode가 자동 리소스 경계를 어떻게 적용하는지 설명하기 위해 colocated_join Quickstart를 기반으로 한 아래 쿼리를 고려해 보세요. 이 쿼리가 일반 MSE로 프로덕션에서 허용된다면 userFactEvents 테이블의 모든 행을 스캔할 거예요. Lite Mode에서는 구성 가능한 제한(일명 "fetch") 값으로 잎 스테이지에 Sort를 자동으로 추가하므로 전체 스캔이 방지돼요.

SET useMultistageEngine = true;
SET usePhysicalOptimizer = true;
SET useLiteMode = true;

EXPLAIN PLAN FOR WITH ordered_events AS (
  SELECT 
    cityName,
    tripAmount,
    ROW_NUMBER() OVER (
      ORDER BY ts DESC
    ) as row_num
  FROM userFactEvents
),
filtered_events AS (
  SELECT 
    *
  FROM ordered_events
  WHERE row_num < 1000
)
SELECT 
  cityName,
  SUM(tripAmount) as cityTotal
FROM filtered_events
GROUP BY cityName

이 쿼리의 쿼리 계획은 다음과 같아요. window 함수, filtered-events 테이블의 필터, 집계는 Pinot Broker에서 단일 스레드를 사용해 실행돼요. Pinot Broker가 100k 레코드의 lite mode 제한 값으로 구성되었다고 가정해요.

PhysicalAggregate(group=[{0}], agg#0=[$SUM0($1)], aggType=[DIRECT])
  PhysicalFilter(condition=[<($3, 1000)])
    PhysicalWindow(window#0=[window(order by [2 DESC] rows between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
      PhysicalExchange(exchangeStrategy=[SINGLETON_EXCHANGE], collation=[[2 DESC]])
        PhysicalSort(fetch=[100000], collation=[[2 DESC]])  <== added by Lite Mode
          PhysicalProject(cityName=[$3], tripAmount=[$7], ts=[$9])
            PhysicalTableScan(table=[[default, userFactEvents]])

Lite Mode 활성화

Lite Mode를 사용하려면 다음 쿼리 옵션을 사용할 수 있어요.

SET useMultistageEngine=true;
SET usePhysicalOptimizer=true;  -- enables the new Physical MSE Query Optimizer
SET useLiteMode=true;           -- enables Lite Mode

Pinot Servers에서 비-잎 스테이지 실행

기본적으로 Lite Mode는 비-잎 스테이지를 Broker에서 실행해요. 비-잎 스테이지를 Pinot Servers에서 실행하려면 다음 쿼리 옵션을 false로 설정할 수 있어요. 이 경우 비-잎 스테이지에 대해 임의의 서버가 선택돼요.

SET runInBroker=false;

구성

Pinot Broker에서 다음 구성을 설정할 수 있어요.

구성 키 Default 설명
pinot.broker.multistage.lite.mode.leaf.stage.limit 100000 서버의 특정 잎 스테이지 인스턴스가 반환할 수 있는 최대 레코드 수. 이는 쿼리 옵션 liteModeLeafStageLimit의 기본값을 제어해요. 100k 레코드 이하를 권장해요.
pinot.broker.multistage.lite.mode.leaf.stage.fanOutAdjustedLimit -1 양수로 설정하면 Pinot는 이 값을 잎 스테이지에 할당된 작업자 수로 나누고 그 몫을 작업자당 하드 제한으로 사용해요. 이는 쿼리 옵션 liteModeLeafStageFanOutAdjustedLimit의 기본값을 제어해요.
pinot.broker.multistage.use.lite.mode false 쿼리 옵션 useLiteMode의 기본값.
pinot.broker.multistage.run.in.broker true 비-잎 스테이지를 기본적으로 broker에서 실행할지 여부. 이는 쿼리 옵션 runInBroker의 기본값을 제어해요.

암시적 잎-스테이지 제한에 대한 런타임 경고

Lite Mode는 SQL 텍스트에 LIMIT이 포함되지 않아도 잎-스테이지 하드 제한을 적용할 수 있어요. 이는 Pinot가 계획 중에 잎-스테이지 PhysicalSort(fetch=...)를 추가하거나 잎-스테이지 집계에 동일한 하드 제한을 적용하는 것처럼 암시적 잎-스테이지 제한을 주입할 때 발생해요.

그 암시적 제한이 활성화되면 multi-stage broker 응답에 다음 필드가 포함돼요.

필드 의미
mseLiteLeafStageLimitReached 실행 시점에 최소한 하나의 잎-스테이지 작업자가 암시적 제한에 도달했으면 true. 이 값이 true면 결과가 불완전하며 Pinot는 partialResult=true도 설정해요.
mseLiteLeafStageEffectiveLimit 암시적 잎-스테이지 상한에 사용된 실제 작업자당 제한. 이 필드는 Pinot가 암시적 제한을 주입했을 때만 존재해요.
mseLiteFanOutAdjustedLimitApplied 유효 제한이 Pinot가 잎-스테이지 작업자들 사이에 나눈 뒤 liteModeLeafStageFanOutAdjustedLimit에서 나온 것이면 true. false는 Pinot가 일반 liteModeLeafStageLimit 값을 사용했음을 의미해요. 이 필드는 Pinot가 암시적 제한을 주입했을 때만 존재해요.

Pinot가 암시적 제한을 주입했지만 실제로 도달한 작업자가 없다면 mseLiteLeafStageLimitReached는 false로 유지되고 Lite Mode는 이런 이유로 응답을 부분(partial)으로 표시하지 않아요.

암시적 제한이 바인딩되는 경우의 응답 예시:

{
  "numRowsResultSet": 8,
  "partialResult": true,
  "mseLiteLeafStageLimitReached": true,
  "mseLiteLeafStageEffectiveLimit": 2,
  "mseLiteFanOutAdjustedLimitApplied": false
}

FAQ

Q1: Lite Mode는 무엇을 위한 것인가요?

Lite Mode는 Uber가 기여했으며 그들의 Presto over Pinot 아키텍처에서 영감을 받았어요. Lite Mode는 더 엄격한 스캔 및 리소스 경계를 가진 선택적 실행 모드로, 사용자가 고급 SQL 기능(window functions, subqueries 등)은 필요하지만 조인 또는 CTE의 완전히 분산된 실행은 필요하지 않은 사용 사례를 위해 설계되었어요. 이를 Single-Stage Engine의 고급 버전으로 생각할 수 있어요.

Q2: 비-잎 스테이지에 왜 broker에서 단일 스레드를 사용하나요?

단일 스레드, 더 정확히는 단일 Operator Chain을 사용하면 전체 스테이지를 Exchange 없이 실행할 수 있어요. 또한 설계를 단순하게 유지하고 성능과 디버깅에 대해 추론하기 쉽게 해요.

Q3: Lite Mode를 고-QPS 사용 사례를 위한 서버/세그먼트 프루닝과 함께 사용할 수 있나요?

네, Table Config에서 여기 설명한 대로 segmentPrunerTypes를 설정하면 세그먼트와 서버가 프루닝돼요. 이를 사용해 Read QPS를 확장할 수 있어요.

더 알아보기 (Learn more)