역 인덱스
역 인덱스 (Inverted Index)
Apache Pinot용 inverted index 구성에 대해 설명해요.
출처: 문서
본문
forward index는 문서 ID(행)를 값으로 매핑해요. inverted index는 이 매핑을 반대로 해요: 값을 포함하는 문서 ID 집합으로 값을 매핑하죠. EQ, IN, GT, LT, BETWEEN 같은 조건자로 컬럼으로 자주 필터링할 때 inverted index를 추가하면 쿼리 성능이 크게 개선될 수 있어요.
Pinot는 bitmap inverted index와 sorted inverted index 두 가지 유형의 inverted index를 지원해요.
언제 사용하나 (When to use)
WHERE 절 필터에 자주 나타나는 컬럼, 특히 등식 및 멤버십 조건자에 inverted index를 사용하세요. inverted index는 성능 튜닝의 좋은 출발점이에요.
- 자주 필터링되는 정렬되지 않은 컬럼에는 bitmap inverted index를 사용하세요.
- 대부분의 쿼리가 같은 컬럼으로 필터링할 때(컬럼이 정렬되면 자동으로) sorted inverted index를 사용하세요.
지원 컬럼 유형
Bitmap inverted index는 MAP을 제외한 모든 데이터 유형에서 지원돼요: INT, LONG, FLOAT, DOUBLE, BIG_DECIMAL, BOOLEAN, TIMESTAMP, STRING, JSON, BYTES. 단일 값과 다중 값 컬럼 모두 지원돼요.
Bitmap inverted index
정렬되지 않은 컬럼에 inverted index가 활성화되면 Pinot는 각 값에서 문서 ID 비트맵으로의 매핑을 유지해요. 이는 값 조회를 상수 시간 O(1)으로 만들어요.
구성
bitmap inverted index를 활성화하는 권장 방법:
{
"fieldConfigList": [
{
"name": "playerName",
"indexes": {
"inverted": {}
}
}
]
}
Bitmap inverted index는 사전 ID가 필요해요. 컬럼의 forward index를 RAW로 유지하려면 fieldConfigList에서 RAW forward index와 독립형 사전을 함께 구성하세요.
{
"fieldConfigList": [
{
"name": "playerName",
"encodingType": "RAW",
"indexes": {
"dictionary": {},
"inverted": {}
}
}
]
}
Pinot는 forward index를 RAW로 유지하고 inverted index에 독립형 사전을 사용해요. 필드 수준 RAW 구성은 필드 수준 인덱스가 요구하고 레거시 no-dictionary 설정이 덮어쓰지 않을 때 사전이 활성화된 상태로 해석될 수도 있지만, 명시적 dictionary 블록이 기존 테이블 구성의 안전한 마이그레이션 형태예요.
inverted index를 활성화하는 동안 컬럼을 레거시 tableIndexConfig.noDictionaryColumns나 tableIndexConfig.noDictionaryConfig에 두지 마세요. 그 설정들은 여전히 사전을 비활성화하므로 Pinot는 공유 사전을 만들지 않고 테이블 구성을 거부해요.
이전 구성
{
"tableIndexConfig": {
"invertedIndexColumns": [
"playerName"
]
}
}
인덱스가 생성되는 시점
Breaking Change (Apache Pinot PR #17951): 이 버전부터
indexingConfig.createInvertedIndexDuringSegmentGeneration구성 플래그는 더 이상 인정되지 않아요. inverted index는 구성되면 다른 인덱스 유형과 일관되게 항상 세그먼트 생성 중에 생성돼요. 이전에 inverted index 생성을 세그먼트 로드 시간으로 연기하는 것에 의존했다면 동작이 변경되었어요.
기본적으로 bitmap inverted index는 세그먼트 생성 중에 생성돼요. 이전에는 이 동작이 테이블 구성 옵션 indexingConfig.createInvertedIndexDuringSegmentGeneration(기본값 false)으로 제어되었지만, 그 플래그는 이제 더 이상 사용되지 않고 무시돼요.
Sorted inverted index
컬럼이 정렬되면 Pinot는 효율적인 필터링을 위해 정렬된 forward index를 활용할 수 있어요. 동작은 컬럼이 사전 인코딩되었는지에 따라 달라져요.
사전 인코딩된 정렬 컬럼: 컬럼이 정렬되고 사전 인코딩되면 Pinot는 inverted index 역할도 하는 런-길이 인코딩이 있는 정렬된 forward index를 사용해요. 이는 자동으로 발생하며 추가 구성이 필요 없어요. 정렬된 inverted index는 O(log n) 조회 시간을 제공하고 데이터 지역성의 혜택을 받아요.
예를 들어 쿼리가 정렬된 memberId 컬럼으로 필터링하면 Pinot는 이진 검색으로 필터 값과 일치하는 문서 ID 범위를 찾아요. 그 문서들에 대한 후속 컬럼 스캔은 일치하는 행이 연속적으로 저장되어 있으므로 데이터 지역성의 혜택을 받아요.
Raw(no-dictionary) 정렬 컬럼: Apache Pinot 1.3.0부터 raw 컬럼은 사전이나 inverted index를 강제하지 않고 정렬 컬럼으로 구성될 수 있어요. 이전에는 raw 컬럼을 정렬로 지정하면 Pinot가 사전과 inverted index를 강제 추가해 raw 인코딩의 저장 혜택을 상쇄했어요. 이제 정렬 순서 메타데이터를 유지하면서 효율적인 저장으로 정렬된 raw 컬럼(예: 타임스탬프 컬럼)을 가질 수 있어요.
사전 인코딩 컬럼의 정렬된 inverted index는 bitmap inverted index보다 더 나은 성능을 제공하지만 각 세그먼트 내 데이터가 물리적으로 정렬된 컬럼에만 적용될 수 있어요. 정렬된 raw 컬럼은 사전 인코딩의 오버헤드 없이 정렬된 데이터에 효율적인 저장을 제공해요.
쿼리 예시
등식 필터:
SELECT COUNT(*)
FROM baseballStats
WHERE playerName = 'Barry Bonds'
IN 필터:
SELECT yearID, hits, homeRuns
FROM baseballStats
WHERE teamID IN ('NYA', 'BOS', 'LAD')
ORDER BY yearID
집계가 있는 필터:
SELECT teamID, SUM(hits) AS totalHits
FROM baseballStats
WHERE league = 'NL'
GROUP BY teamID
ORDER BY totalHits DESC
LIMIT 10
제한 사항
- Bitmap inverted index는 사전 ID가 필요하지만, Pinot는 사전 인코딩된 forward index나 RAW forward index를 위해 구체화된 독립형 사전으로 이를 충족할 수 있어요. 레거시 no-dictionary 구성이 그 컬럼의 사전을 비활성화해서는 안 돼요.
- 정렬된 inverted index(사전 인코딩 컬럼)는 각 세그먼트 내 데이터가 물리적으로 정렬된 컬럼에서만 동작해요.
- 정렬된 raw 컬럼(no-dictionary)은 inverted index 없이도 정렬 메타데이터를 지원해요.
- MAP 컬럼은 지원되지 않아요.