Software Development/Big Data

[Pinot] 가볍게 Star Tree Index 정리

루ㅌ 2026. 8. 19. 23:10

Pinot Star-Tree 인덱스란?

  • 여러 차원 조합에 대한 집계 결과를 미리 만들어 두고, 쿼리마다 필요한 pre-aggregated record만 빠르게 골라 읽기 위한 다차원 인덱스
  • pre-aggregated result를 이용해 처리해야 할 값의 수를 줄이는 구조

Star-Tree가 왜 필요한가?

예를 들어 이런 이벤트 테이블이 있다고 하자.

row country device browser revenue
1 US mobile Chrome 40
2 US mobile Safari 50
3 US desktop Chrome 80
4 US desktop Firefox 70
5 KR mobile Chrome 40
6 KR mobile Safari 20
7 US mobile Chrome 100
8 KR desktop Chrome 30
SELECT browser, SUM(revenue)
FROM events
WHERE country = 'US'
GROUP BY browser;

 

위 쿼리를 실행하는데, Star-Tree가 없다면 Pinot은 inverted index 등을 이용해 country='US'인 row를 빨리 찾을 수 있다.

하지만 결국 선택된 row들의 revenue를 읽고 browser별로 aggregation해야 한다.

즉, 검색 속도를 Inverted Index를 통해서 빠르게 해주지만, 결과적으로 aggregation 비용을 줄여주진 않는다.

그렇다고 모든 차원에 대해서 pre-aggregation을 하자니, 차원 조합이 다양해지면 저장공간이 폭발할 수 있다. Star-Tree는 이 둘 사이의 trade-off를 목표로 한다.

 

Star-Tree는 먼저 데이터를 집계한다.

다음 Star-Tree를 설정했다고 하자.

{
  "starTreeIndexConfigs": [
    {
      "dimensionsSplitOrder": [
        "country",
        "device",
        "browser"
      ],
      "skipStarNodeCreationForDimensions": [],
      "functionColumnPairs": [
        "SUM__revenue"
      ],
      "maxLeafRecords": 1
    }
  ]
}

 

Pinot은 dimensionsSplitOrder에 포함된 dimension들로 데이터를 projection하고, 같은 dimension combination을 가진 record들의 metric을 설정된 aggregation function으로 합친다.

이 aggregated document들은 원본 document와 별도로 Star-Tree용 document로 저장된다.

Star-Tree 입장에서는 처음부터 다음 7개 aggregated record를 가지고 시작한다.

 

country device browser SUM(revenue)
US mobile Chrome 140
US mobile Safari 50
US desktop Chrome 80
US desktop Firefox 70
KR mobile Chrome 40
KR mobile Safari 20
KR desktop Chrome 30

여기까지는 그냥 GROUP BY country, device, browser로 미리 계산하는 것과 비슷하다.

이 다음에 설명할 내용이 Star-Tree의 차별적인 특징이다.

 

* Star Node가 핵심이다.

Pinot 공식 정의에서 Star Node는 현재 level에서 split하는 dimension을 제거하고 집계한 pre-aggregated records를 담는 특별한 child다.

즉, 'country = *'는 country 값을 신경 쓰지 않고 모든 country를 합친 것이다.

그러면, 데이터는 다음과 같이 만들어진다.

country device browser SUM(revenue)
* mobile Chrome 180
* mobile Safari 70
* desktop Chrome 110
* desktop Firefox 70

 

그래서 'country=*' 아래에서 다시 device로 나눌 수 있다.

Star Tree Index

 

위 그림의 D2가 device 디멘젼이라면, 해당 Star child는 'device=*'이다.
여기에는 device를 없애서 browser 기준으로 aggregate하면 다음과 같은 결과를 저장한다.
  • US, *, Chrome, 220
  • US, *, Safari, 50
  • US, *, Firefox, 70

결국 위에 있던 쿼리를 다시 본다면, country = US, device=*에 해당하는 노드를 타면 바로 값을 조회할 수 있다.

SELECT browser, SUM(revenue)
FROM events
WHERE country = 'US'
GROUP BY browser;

 

dimensionsSplitOrder

"dimensionsSplitOrder": [
  "country",
  "device",
  "browser"
]

첫째, 어떤 dimension을 Star-Tree 안에 넣을 것인가

둘째, 어떤 순서로 tree를 split할 것인가

를 설정하게 된다. 즉, 순서가 다르면 전혀 다른 방식으로 트리를 만들게 된다. 따라서 cardinality, data distribution, query workload에 따라 index 크기와 traversal 비용이 달라질 수 있다.

 

skipStarNodeCreationForDimensions 설정

만일 다음과 같이 설정했다고 가정한다면, device=*에 대한 Star Node가 생성되지 않는다. 즉, Star Node가 없다면 미리 계산된게 없기 때문에 데이터 저장 공간을 줄일 수 있지만, 반대로 해당 질의에 대해서는 런타임에 계산이 필요하다. 

"skipStarNodeCreationForDimensions": ["device"]

 

maxLeafRecords 설정

maxLeafRecords는 leaf node가 설정된 레코드보다 크면, 다음 디멘젼으로 더 split된다.

예를 들어, 50,000으로 설정했고, country = US 아래의 레코드가 4개니까 더 이상 노드를 생성하지 않는다.

현재 예시에서는 1로 설정했으니, 모든 leaf node에 대해서 생성하게 된다.