스테이지 이해하기
스테이지 이해하기 (Understanding Stages)
멀티-스테이지 스테이지에 대해 더 배우고 쿼리 계획에서 스테이지를 추출하는 방법을 알아봐요.
출처: 문서
본문
스테이지에 대한 심층 설명
Multi-stage 쿼리 엔진 참조 문서에서 설명했듯이, multi-stage 쿼리 엔진은 쿼리를 여러 스테이지로 나눠요. 각 스테이지는 쿼리 계획의 부분집합에 해당하며 독립적으로 실행돼요. 스테이지는 트리 모양 구조로 연결되며, 한 스테이지의 출력이 다른 스테이지의 입력이 돼요. 트리의 루트에 있는 스테이지는 최종 결과를 클라이언트로 보내요. 트리의 잎(leaf)에 있는 스테이지는 테이블에서 읽어요. 중간 스테이지는 데이터를 처리해 다음 스테이지로 보내요.
broker가 쿼리를 받으면 쿼리 계획을 생성해요. 이는 각 노드가 연산자(operator)인 트리 모양 구조예요. 그런 다음 계획을 최적화해, 의미적으로 동등하지만(같은 행을 반환) 더 효율적인 계획을 만들기 위해 노드를 이동하고 변경해요. 이 단계에서 broker는 계획의 노드에 색을 칠해 스테이지에 할당해요. 또한 broker는 각 스테이지에 병렬성(parallelism)을 할당하고 어떤 서버가 각 스테이지를 실행할지 정의해요. 예를 들어 스테이지의 병렬성이 10이라면 최대 10개 서버가 그 스테이지를 병렬로 실행해요. 하나의 서버가 여러 스테이지를 병렬로 실행할 수 있고 심지어 같은 스테이지의 여러 인스턴스도 병렬로 실행할 수 있어요.
스테이지는 각 스테이지의 고유 식별자인 스테이지 ID(stage ID)로 식별돼요. 현재 구현에서 스테이지 ID는 숫자이며 루트 스테이지의 스테이지 ID는 0이에요(미래에 바뀔 수 있음).
현재 구현에는 언급할 가치가 있는 몇 가지 속성이 있어요:
- 잎 스테이지는 single-stage 쿼리 엔진의 약간 수정된 버전을 실행해요. 따라서 이 스테이지는 항상 중간 스테이지에서 실행되는 조인이나 집계를 실행할 수 없어요.
- 중간 스테이지는 multi-stage 쿼리 엔진을 위해 새로 만들어진 새 쿼리 실행 엔진을 사용해 연산을 실행해요. 이것이 single-stage 쿼리 엔진에서 지원되는 일부 함수가 multi-stage 쿼리 엔진에서는 지원되지 않고 그 반대도 마찬가지인 이유예요.
- 중간 스테이지는 하나의 조인, 하나의 윈도우 함수 또는 하나의 집합 연산만 가질 수 있어요. 쿼리에 이러한 연산이 두 개 이상 있으면 broker가 각각 하나의 연산을 가진 여러 스테이지를 만들게 돼요.
쿼리 계획에서 스테이지 추출하기
Explain Plan (Multi-Stage)에서 설명했듯이 EXPLAIN PLAN 구문을 사용해 쿼리의 논리 계획을 얻을 수 있어요. 이 논리 계획을 사용해 쿼리의 스테이지를 추출할 수 있어요.
예를 들어 쿼리가 다음과 같다면:
explain plan for
select customer.c_address, orders.o_shippriority
from customer
join orders
on customer.c_custkey = orders.o_custkey
limit 10
EXPLAIN PLAN 명령의 가능한 출력은 다음과 같아요:
LogicalSort(offset=[0], fetch=[10])
PinotLogicalSortExchange(distribution=[hash], collation=[[]], isSortOnSender=[false], isSortOnReceiver=[false])
LogicalSort(fetch=[10])
LogicalProject(c_address=[$0], o_shippriority=[$3])
LogicalJoin(condition=[=($1, $2)], joinType=[inner])
PinotLogicalExchange(distribution=[hash[1]])
LogicalProject(c_address=[$4], c_custkey=[$6])
LogicalTableScan(table=[[default, customer]])
PinotLogicalExchange(distribution=[hash[0]])
LogicalProject(o_custkey=[$5], o_shippriority=[$10])
LogicalTableScan(table=[[default, orders]])
모든 쿼리와 마찬가지로 논리 계획은 트리 모양 구조를 형성해요. 이 기본 explain 형식에서는 트리 모양 구조가 들여쓰기로 표현돼요. 트리의 루트는 첫 번째 줄이며, 이는 마지막으로 실행되는 연산자이고 루트 스테이지를 표시해요. 스테이지 사이의 경계는 PinotLogicalExchange 연산자예요. 위 예시에는 네 개의 스테이지가 있어요:
- 루트 스테이지는 연산자 루트의
LogicalSort연산자로 시작해PinotLogicalSortExchange연산자로 끝나요. 이는 마지막으로 실행되는 스테이지이며 broker에서 실행되는 유일한 스테이지예요. 계산되면 결과를 직접 클라이언트로 보내요. - 다음 스테이지는 이
PinotLogicalSortExchange연산자로 시작해LogicalSort연산자,LogicalProject연산자,LogicalJoin연산자, 두 개의PinotLogicalExchange연산자를 포함해요. 이 스테이지는 분명히 루트 스테이지가 아니고 세그먼트에서 데이터를 읽지 않으므로 잎 스테이지도 아니에요. 따라서 중간 스테이지여야 해요. - 조인에는 두 개의 자식이 있는데, 이는
PinotLogicalExchange연산자예요. 이 특정 경우에는 양쪽이 매우 비슷해요.PinotLogicalExchange연산자로 시작해LogicalTableScan연산자로 끝나요.LogicalTableScan연산자로 끝나는 모든 스테이지는 잎 스테이지예요.
이제 스테이지를 식별했으니, multi-stage explain 계획 해석을 통해 각 스테이지가 무엇을 하는지 이해할 수 있어요.