포워드 인덱스

포워드 인덱스 (Forward Index)

forward index는 Pinot가 각 컬럼의 값을 저장하는 데 사용하는 메커니즘이에요. 개념적으로 forward index는 문서 ID(행 인덱스라고도 함)에서 각 행의 실제 컬럼 값으로의 매핑이라고 생각할 수 있어요.

출처: 문서

본문

forward index는 기본적으로 활성화되어 있어요. 즉 컬럼은 명시적으로 비활성화되지 않으면 forward index를 갖게 돼요. forward index를 비활성화하면 다른 인덱스가 필요한 데이터 패턴을 충분히 커버할 때 저장 공간을 절약할 수 있어요. forward index 비활성화 방법과 그 영향은 Disabling the Forward Index를 참조하세요.

사전 인코딩 vs raw 값

forward index의 구현 방식은 인덱스 인코딩과 컬럼의 정렬 여부에 따라 달라져요.

인코딩이 RAW로 설정되면 forward index는 배열로 구현되며, 인덱스는 문서 ID에 해당하고 값은 실제 행 값을 나타내요. 자세한 내용은 raw value forward index 섹션을 참조하세요.

DICTIONARY 인코딩의 경우 forward index는 실제 행 값을 저장하지 않고 사전 ID를 저장해요. 이는 값을 읽을 때 추가적인 간접(indirection) 단계를 도입하지만, 컬럼의 고유 값 수가 행 수보다 크게 작을 때 더 효율적인 물리 레이아웃을 허용해요.

DICTIONARY 인코딩은 세그먼트가 인덱싱된 컬럼으로 정렬되면 더 효율적일 수 있어요. dictionary encoded forward index와 sorted forward index에서 자세히 볼 수 있어요.

컬럼이 사전 인코딩을 사용해야 하는지 raw 값 인코딩을 사용해야 하는지 결정할 때 다음 비교 표가 도움이 될 수 있어요.

Dictionary Raw Value
낮거나 중간 카디널리티일 때 압축 제공. 패딩 오버헤드 제거.
inverted, FST/IFST 같은 사전 기반 인덱스를 직접 지원. forward index를 RAW로 유지하면서 독립형 사전이 사전 기반 보조 인덱스를 지원할 수 있음.
간접 참조 한 단계 추가로 디스크 탐색 증가 가능. 추가 간접 참조 제거로 관심 있는 모든 문서가 연속적일 때 좋음.
문자열의 경우 사전의 모든 값을 같은 길이로 만들기 위해 패딩 추가. 선택된 문서들이 공간적 지역성을 가지지 않을 때 청크 압축 해제 오버헤드.

사전 인코딩된 forward index와 비트 압축 (기본값)

이 접근 방식에서 컬럼의 각 고유 값에는 ID가 할당되고, 이 ID를 해당 값으로 매핑하는 사전이 구축돼요. 기본 forward index는 실제 값 대신 이러한 비트 압축된 ID를 저장해요. 이 방법은 고유 값이 적은 컬럼을 다룰 때 특히 효과적이며 공간 효율성을 크게 개선해요.

integer와 string 유형의 두 컬럼에 대한 사전 인코딩을 보여주는 다이어그램: colA의 경우 사전 인코딩이 중복 값에 대해 상당한 공간을 절약했어요. colB처럼 대부분 고유한 값을 가진 컬럼의 경우 압축 효과는 제한적이고 패딩 오버헤드가 클 수 있어요.

사전 인코딩에 대해 더 알려면 Dictionary index를 참조하세요.

다중 값 컬럼에 사전 인코딩된 forward index를 사용할 때 반복되는 다중 값 항목의 forward index를 더 압축하려면 MV_ENTRY_DICT 압축 유형을 활성화하세요. 이는 다중 값 항목에 또 다른 사전 인코딩 계층을 추가해요. 예를 들어 사실 테이블과 차원 테이블을 사전에 조인해 차원 테이블의 다중 값 항목이 사실 테이블과 조인 후 반복되는 경우 유용할 수 있어요.

이것은 다음 파라미터로 활성화할 수 있어요.

Parameter Default Description
dictIdCompressionType null 사전 인코딩된 forward index에 사용될 압축.

런-길이 인코딩이 있는 정렬된 forward index

컬럼이 물리적으로 정렬되면 Pinot는 사전 인코딩 위에 구축되는 런-길이 인코딩이 있는 정렬된 forward index를 사용해요. 각 문서 ID에 대해 사전 ID를 저장하는 대신 각 고유 값에 대해 시작 및 끝 문서 ID 쌍을 저장해요.

(간단히 하기 위해 이 다이어그램은 사전 인코딩 계층을 포함하지 않음.)

정렬된 forward index는 효율적인 압축과 데이터 지역성의 이점을 제공하며 inverted index 역할도 할 수 있어요. 두 가지 조건이 충족될 때 활성화돼요: 세그먼트가 그 컬럼으로 정렬되어 있고, 그 컬럼에 대해 사전이 활성화되어 있어야 해요. 사전 활성화에 대한 자세한 내용은 dictionary documentation을 참조하세요.

여러 세그먼트를 다룰 때 각 세그먼트 내에서 데이터가 정렬되도록 하는 것이 중요해요. 세그먼트 간 정렬은 필요하지 않아요.

세그먼트가 특정 컬럼으로 정렬되도록 보장하려면 다음 단계를 따르세요.

  • 실시간 테이블의 경우 tableIndexConfig.sortedColumn 속성을 사용하세요. 그 배열에 정확히 하나의 컬럼이 지정되면 Pinot는 커밋 시 세그먼트를 그 컬럼으로 정렬해요.
  • offline 테이블의 경우 Pinot에 수집하기 전에 지정된 컬럼으로 데이터를 사전 정렬해야 해요.

중요: offline 테이블의 경우 tableIndexConfig.sortedColumn 속성은 실제로 무시된다는 점을 기억하세요.

또한 온라인 테이블의 경우 이 속성이 JSON 배열로 지정되지만, 기껏해야 하나의 컬럼만 포함해야 해요. 둘 이상의 컬럼이 있는 배열을 사용하는 것은 잘못이며 배열에 나열된 모든 컬럼으로 세그먼트가 정렬되지 않을 거예요.

실시간 세그먼트가 커밋되면 행은 정렬 컬럼으로 정렬되고 offline 세그먼트로 변환돼요.

offline 세그먼트 생성 중(실시간 세그먼트가 커밋될 때도 적용됨) Pinot는 각 컬럼의 데이터를 스캔해요. 컬럼 내 모든 값이 오름차순 정렬된 것을 감지하면 Pinot는 세그먼트가 그 특정 컬럼으로 정렬되었다고 결론 내려요. 이것이 둘 이상의 컬럼에서 발생하면 모두 정렬 컬럼으로 간주돼요. 따라서 세그먼트가 컬럼으로 정렬되었는지 여부는 세그먼트 내 실제 데이터 분포에만 의존하며 sortedColumn 속성의 값은 완전히 무시돼요. 이 접근 방식은 같은 테이블에 속한 두 세그먼트가 다른 수의 정렬 컬럼을 가질 수 있음을 의미하기도 해요. 세그먼트에 행이 하나만 있는 극단적인 시나리오에서는 Pinot가 그 세그먼트의 모든 컬럼을 정렬 컬럼으로 간주해요.

이 개념을 보여주는 테이블 구성 예시:

{
    "tableIndexConfig": {
        "sortedColumn": [
            "column_name"
        ],
        ...
    }
}
정렬 상태 확인

다음을 실행해 세그먼트의 컬럼 정렬 상태를 확인할 수 있어요.

$ grep memberId <segment_name>/v3/metadata.properties | grep isSorted
column.memberId.isSorted = true

또는 offline 테이블과 실시간 테이블의 커밋된 세그먼트의 경우 getServerMetadata 엔드포인트에서 정렬 상태를 검색할 수 있어요. 다음 예시는 Batch Quick Start을 기반으로 해요.

curl -X GET \
  "http://localhost:9000/segments/baseballStats/metadata?columns=playerID&columns=teamID" \
  -H "accept: application/json" 2>/dev/null | \
  jq -c  '.[] | . as $parent |  
          .columns[] | 
          [$parent .segmentName, .columnName, .sorted]'
["baseballStats_OFFLINE_0","teamID",false]
["baseballStats_OFFLINE_0","playerID",false]

Raw 값 forward index

raw 값 forward index는 ID 대신 실제 값을 저장해요. 이는 값을 가져올 때 사전 조회가 필요 없음을 의미하며, 쿼리 성능을 개선할 수 있어요. Raw forward index는 사전 인코딩이 큰 압축 혜택을 제공하지 않는 많은 고유 값을 가진 컬럼에 특히 효과적이에요.

다이어그램처럼 사전 인코딩은 사전 조회를 위한 많은 랜덤 메모리 접근을 초래할 수 있어요. 대조적으로 raw 값 forward index는 순차 값 스캔을 허용해 적절히 적용하면 쿼리 성능을 향상시킬 수 있어요.

참고: RAW forward index는 여전히 사전 ID나 사전 값이 필요한 보조 인덱스와 함께 사용할 수 있어요. RAW 컬럼에 bitmap inverted index나 FST/IFST 같은 사전 기반 인덱스를 활성화하면 Pinot는 forward index를 RAW로 유지하고 보조 인덱스용 독립형 사전을 구체화해요. 기존 테이블 구성 업데이트의 경우 레거시 tableIndexConfig.noDictionaryColumns나 tableIndexConfig.noDictionaryConfig에서 컬럼을 제거하고 fieldConfigList.indexes.dictionary 아래에 사전을 선언하세요. 그렇지 않으면 검증이 사전을 여전히 비활성으로 처리해요. RAW forward index에서 값을 읽으려면 전체 청크를 메모리로 읽고 압축을 해제해야 하므로 무거운 랜덤 읽기에는 적합하지 않아요.

정렬된 raw 컬럼: Pinot 1.3.0부터 raw 컬럼은 inverted index를 강제하지 않고 정렬 컬럼으로 구성될 수 있어요. 이전에는 컬럼을 정렬 및 no-dictionary로 구성하면 Pinot가 inverted index를 강제 추가해 raw 인코딩의 저장 이점을 상쇄했어요. 이제 사전 인코딩이나 inverted index 없이 시간 정렬 raw 컬럼(예: 타임스탬프 컬럼)을 가질 수 있어 정렬 순서 메타데이터를 유지하면서 효율적인 저장이 가능해요.

RAW forward-index 인코딩은 encodingType을 RAW로 설정해 선택돼요. 사전이 비활성화되면 컬럼은 전통적인 no-dictionary RAW 컬럼이에요. 사전이 명시적으로 활성화되거나 보조 인덱스가 필요해서 유지되면 Pinot는 독립형 사전과 함께 RAW forward 값을 저장해요. 자세한 내용은 dictionary documentation과 field config list를 참조하세요.

참고: 같은 RAW 컬럼에서 레거시 no-dictionary 구성과 사전 기반 보조 인덱스를 결합하지 마세요. noDictionaryColumns와 noDictionaryConfig는 여전히 사전을 비활성화하고, 사전 ID나 사전 값이 필요한 인덱스를 검증이 거부하게 해요.

raw 형식을 사용할 때 다음 파라미터를 구성할 수 있어요.

Parameter Default Description
chunkCompressionType null 사용될 압축. 릴리스 1.2.0 이후 compressionCodec로 대체됨.
compressionCodec null 사용될 압축. 릴리스 1.2.0에서 도입.
codecSpec null 단일 값 고정 폭 RAW 컬럼을 위한 codec 파이프라인. null이 아닌 값은 V7 forward-index 형식을 선택함; compressionCodec와 결합 불가.
deriveNumDocsPerChunk false 가변 길이 값(문자열이나 바이트)을 저장할 때 동작 수정.
rawIndexWriterVersion 4 명시적으로 덮어쓰지 않을 때 사용되는 raw forward-index writer 버전.
targetDocsPerChunk 1000 청크당 대상 문서 수.
targetMaxChunkSize 1MB 대상 최대 청크 크기.

compressionCodec 파라미터는 raw 인코딩 컬럼에 대해 다음 유효 값을 가져요.

  • PASS_THROUGH — 압축 없음
  • SNAPPY — 빠른 압축, 중간 정도 비율
  • ZSTANDARD — 높은 압축 비율, Snappy보다 느림
  • LZ4 — 빠른 압축, 속도와 비율의 좋은 균형
  • GZIP — 높은 압축 비율(릴리스 1.2.0에서 도입)
  • null(JSON null 값, "null" 아님)이 기본값. 이 경우 메트릭에는 PASS_THROUGH, 다른 컬럼에는 LZ4가 사용됨.

사전 인코딩된 다중 값 forward index에는 추가 압축 유형이 하나 더 있어요.

  • MV_ENTRY_DICT — 반복되는 다중 값 항목에 2차 사전 인코딩을 추가. 사실 테이블과 차원 테이블을 사전 조인하여 차원 테이블의 다중 값 항목이 반복되는 경우 유용.

특정 사용 사례에 사용할 수 있는 추가 특수 목적 압축 codec이 있어요.

  • CLP, CLPV2, CLPV2_ZSTD, CLPV2_LZ4 — 로그 라인 데이터용 CLP 기반 압축 codec. 범용이 아니며 로그 데이터 패턴에 대한 특수 처리가 있음.
  • DELTA, DELTADELTA — 순차적이거나 거의 순차적인 값을 가진 숫자 컬럼용 델타 인코딩 codec. 특정 상황에서 자동 적용되며 일반적으로 직접 구성되지 않음.

deriveNumDocsPerChunk는 string, big decimal, bytes 등 데이터 유형이 가변 길이일 수 있을 때만 사용돼요. 기본적으로 Pinot는 경험적으로 선택된 고정 요소 수를 사용해요. true로 변경하면 컬럼 데이터에 의존하는 휴리스틱 값을 사용해요.

rawIndexWriterVersion은 레거시 raw index를 만드는 데 사용되는 알고리즘을 변경해요. 이는 실제 데이터 레이아웃을 바꾸지만 현대 Pinot 버전은 이전 버전에 작성된 인덱스를 읽을 수 있어요. Pinot는 이제 새 raw forward index를 버전 4로 기본 설정해요. 이 설정으로 선택할 수 있는 최신 버전은 6이에요; codecSpec은 V7을 별도로 선택해요.

V6 Forward Index 형식(델타 인코딩된 청크 헤더): Pinot 1.3.0부터 버전 6은 forward index 청크 헤더에 대한 개선된 압축을 제공해요. V6 형식은 청크 헤더를 델타 인코딩해 전체 오프셋 대신 개별 항목 크기(누적 오프셋의 델타)를 저장해요. 이 델타 값은 작고 반복적인 정수라 원래 오프셋보다 훨씬 잘 압축돼요. V6는 V4/V5 위의 얇은 계층이에요 — 청크 헤더 인코딩만 다르고 데이터 섹션과 디스크 파일 레이아웃은 동일해요. PASS_THROUGH 압축을 사용하면 압축 없이 델타 인코딩이 이점이 없으므로 V6는 V4 스타일 오프셋으로 대체돼요. 리더는 델타 인코딩된 크기를 단일 순방향 패스로 제자리에서 누적 오프셋으로 변환한 다음 V4의 표준 랜덤 접근 읽기 로직을 변경 없이 재사용해 호환성과 성능을 보장해요.

V7 codec-파이프라인 형식: 단일 값 고정 폭 RAW 컬럼에 fieldConfigList[].indexes.forward.codecSpec을 설정해 자기 설명(self-describing) V7 forward index를 작성하세요. 예를 들어 "encodingType": "RAW"인 정수 컬럼에 "codecSpec": "DELTA,ZSTD(3)"을 사용해요. 압축 전용 파이프라인을 포함한 null이 아닌 codecSpec은 V7을 선택해요. 미설정으로 두면 레거시 compressionCodec 경로를 사용해요. V7에서 targetDocsPerChunk는 적용되지만 rawIndexWriterVersion, deriveNumDocsPerChunk, targetMaxChunkSize는 적용되지 않아요. Pinot는 테이블 구성이 제출될 때 호환되지 않는 컬럼 형태와 옵션을 검증해요.

codecSpec을 활성화하기 전에 세그먼트를 만들거나 읽는 모든 컴포넌트를 업그레이드하세요. V7을 읽을 수 없는 바이너리로 롤백하기 전에 codecSpec을 제거하고 영향을 받은 세그먼트를 레거시 형식으로 재생성하거나 재푸시하세요. 리로드는 서버-로컬 복사본만 다시 씁니다. 딥 스토리지의 변경되지 않은 V7 복사본은 다시 다운로드될 수 있어요. PR #19307 참조.

targetDocsPerChunk는 청크에 저장할 대상 문서 수를 변경해요. rawIndexWriterVersion 2와 3의 경우 청크당 정확히 targetDocsPerChunk를 저장해요. rawIndexWriterVersion 4의 경우 이 구성은 targetMaxChunkSize와 함께 사용되며 청크 크기는 min(lengthOfLongestDocumentInSegment * targetDocsPerChunk, targetMaxChunkSize) 공식으로 결정돼요. 음수 값은 동적 청크 크기 조정을 비활성화하고 정적 targetMaxChunkSize를 사용해요.

targetMaxChunkSize는 대상 최대 청크 크기를 변경해요. rawIndexWriterVersion 2와 3의 경우 deriveNumDocsPerChunk와 함께만 사용될 수 있어요. rawIndexWriterVersion 4의 경우 동적으로 계산된 청크 크기의 상한을 설정해요. targetMaxChunkSize보다 큰 문서는 자체 'huge' 청크가 주어지므로, huge 청크를 피하도록 크기를 정하는 것이 권장돼요.

클러스터 및 인스턴스 기본값

raw forward index에 대한 운영자 관리 기본값을 원하면 Pinot는 로컬 인스턴스 구성이나 ZK 클러스터 구성에서 다음 속성을 읽을 수 있어요.

Property Applies to
pinot.forward.index.default.raw.index.writer.version rawIndexWriterVersion
pinot.forward.index.default.target.max.chunk.size targetMaxChunkSize
pinot.forward.index.default.target.docs.per.chunk targetDocsPerChunk

이 기본값은 해당 속성이 테이블 구성에 명시적으로 설정되지 않았을 때만 사용돼요.

Raw forward index 구성

raw 형식으로 forward index를 구성하는 권장 방법은 위에서 설명한 파라미터들을 indexes.forward 객체에 포함하는 것이에요. 예를 들어:

{
  "tableName": "somePinotTable",
  "fieldConfigList": [
    {
      "name": "playerID",
      "encodingType": "RAW",
      "indexes": {
        "forward": {
          "compressionCodec": "PASS_THROUGH", // or "SNAPPY", "ZSTANDARD", "LZ4" or "GZIP"
          "deriveNumDocsPerChunk": false,
          "rawIndexWriterVersion": 4
        }
      }
    },
    ...
  ],
...
}

forward index를 RAW로 유지하면서 사전 기반 보조 인덱스를 활성화하려면 fieldConfigList에서 사전과 보조 인덱스를 모두 선언하세요.

{
  "tableName": "somePinotTable",
  "fieldConfigList": [
    {
      "name": "playerID",
      "encodingType": "RAW",
      "indexes": {
        "dictionary": {},
        "inverted": {},
        "forward": {
          "compressionCodec": "LZ4"
        }
      }
    },
    ...
  ],
...
}

사용 중단(Deprecated)

raw 형식 파라미터를 구성하는 대체 방법도 있지만 권장되지는 않아요. 이 이전 방법의 세부 사항은 다음과 같아요.

  • chunkCompressionType: 이 파라미터는 fieldConfigList 섹션에서 name과 encodingType의 형제로 정의될 수 있어요.
  • deriveNumDocsPerChunk: deriveNumDocsPerChunkForRawIndex 속성으로 구성할 수 있어요. properties에서 모든 값은 문자열이어야 하므로 유효 값은 "true"와 "false"예요.
  • rawIndexWriterVersion: rawIndexWriterVersion 속성으로 구성할 수 있어요. 마찬가지로 properties의 모든 값은 문자열이어야 하므로 유효 값은 "2", "3" 등이에요.

예를 들어:

{
  "tableName": "somePinotTable",
  "fieldConfigList": [
    {
      "name": "playerID",
      "encodingType": "RAW",
      "chunkCompressionType": "PASS_THROUGH", // it can also be defined here
      "properties": {
        "deriveNumDocsPerChunkForRawIndex": "false", // here the string value has to be used
        "rawIndexWriterVersion": "4" // here the string value has to be used
      }
    },
    ...
  ],
...
}

이 이전 방법은 여전히 지원되지만 이러한 파라미터를 구성하는 권장 방법은 아니에요. 이 이전 방법에 대한 지원을 제거할 계획은 없지만, 향후 추가되는 새 파라미터는 forward JSON 객체에서만 구성 가능할 수 있음을 유의하세요.

forward index 비활성화

전통적으로 forward index는 디스크 세그먼트 파일 형식의 모든 컬럼에 필수 인덱스였어요.

그러나 일부 컬럼은 모든 쿼리에서 WHERE 절의 필터로만 사용될 수 있어요. 이러한 시나리오에서는 다른 인덱스와 세그먼트의 구조가 필요한 SQL 쿼리 기능을 제공할 수 있으므로 forward index가 필요하지 않아요. forward index는 그러한 시나리오에 대해 추가 저장 공간만 차지하므로 이상적으로 해제될 수 있어요.

따라서 사용자에게 저장 공간을 절약할 옵션을 제공하기 위해 forward index를 비활성화하는 노브가 이제 제공돼요.

Pinot 테이블의 하나 이상의 컬럼에 대한 forward index는 다음 제한과 함께 비활성화될 수 있어요.

  • 불변(offline) 세그먼트만 지원.
  • 컬럼에 range index가 있으면 컬럼은 단일 값 유형이고 range index version 2를 사용해야 함.
  • 행 내 중복이 있는 MV 컬럼은 forward index 재생성 시 중복 항목을 잃을 수 있음. MV 행의 데이터 순서도 재생성 시 변경될 수 있음. 같은 시나리오에서 백필(backfill)이 필요함(중복이나 순서 보존을 위해).
  • 리로드 시 forward index 재생성 지원(즉 forward index 비활성 컬럼에 대해 forward index 재활성화)이 필요하면 그 특정 컬럼에 사전과 inverted index가 활성화되어 있어야 함.

정렬된 컬럼은 forward index를 비활성화할 수 있지만 이 작업은 no-op으로 처리되고 인덱스(forward index와 inverted index 역할을 모두 하는)가 생성될 거예요.

table config의 fieldConfigList에서 disabled 속성을 true로 설정해 forward index를 비활성화하세요.

{
  "tableName": "somePinotTable",
  "fieldConfigList": [
    {
      "name":"columnA",
      "indexes": {
        "forward": {
          "disabled": true
        }
      }
    },
    ...
  ],
  ...
}

이전 방식도 여전히 지원되지만 권장되지는 않아요.

"fieldConfigList":[
  {
     "name":"columnA",
     "properties": {
        "forwardIndexDisabled": "true"
      }
  }
]

위 구성이 적용되려면 테이블 리로드 작업이 수행되어야 해요. 컬럼의 다른 인덱스 활성화/비활성화는 일반적인 table config 옵션으로 할 수 있어요.

forward index는 비활성화된 컬럼에 대해 인덱스를 활성화하고 세그먼트를 리로드해 재생성할 수도 있어요. forward index는 컬럼에 사전과 inverted index가 활성화된 경우에만 재생성될 수 있어요. 둘 중 하나가 비활성화되면 forward index를 되찾는 유일한 방법은 offline 작업으로 세그먼트를 재생성하고 데이터를 재푸시/리프레시하는 것이에요.

경고:

다중 값(MV) 컬럼의 경우 forward index 비활성 컬럼의 forward index를 재생성한 후 다음 불변 조건을 유지할 수 없어요.

  • 행 내 MV 값의 순서 보장
  • MV 행 내 항목이 중복되면 중복이 손실됨. offline 작업으로 세그먼트를 재생성하고 데이터를 재푸시/리프레시해 원래 MV 데이터를 중복과 함께 되찾으세요.

향후 두 번째 불변 조건을 제거하기 위해 노력할 거예요.

예시 컬럼 columnA에 대해 forward index를 비활성화한 후 실패할 쿼리의 예는 아래에서 확인할 수 있어요.

Select

forward index 비활성 컬럼은 필터가 추가되어도 SELECT 절에 있을 수 없어요.

SELECT columnA
FROM myTable
    WHERE columnA = 10
SELECT *
FROM myTable

Group By Order By

forward index 비활성 컬럼은 GROUP BY와 ORDER BY 절에 있을 수 없어요. HAVING 절의 일부일 수도 없어요.

SELECT SUM(columnB)
FROM myTable
GROUP BY columnA
SELECT SUM(columnB), columnA
FROM myTable
GROUP BY columnA
ORDER BY columnA
SELECT MIN(columnA)
FROM myTable
GROUP BY columnB
HAVING MIN(columnA) > 100
ORDER BY columnB

집계 쿼리

MIN, MAX, DISTINCTCOUNT, DISTINCTCOUNTHLL 등 집계 함수의 일부는 forward index가 비활성화되어도 동작해요. 다음 같은 다른 집계 함수 중 일부는 동작하지 않아요.

SELECT SUM(columnA), AVG(columnA)
FROM myTable
SELECT MAX(ADD(columnA, columnB))
FROM myTable

Distinct

forward index 비활성 컬럼은 SELECT DISTINCT 절에 있을 수 없어요.

SELECT DISTINCT columnA
FROM myTable

범위 쿼리

필터 절에 >, <, >=, <= 같은 연산자가 포함된 단일 값 컬럼에서 쿼리를 실행하려면 version 2 range index가 있어야 해요. range index가 없으면 아래처럼 그러한 쿼리가 실패해요.

SELECT columnB
FROM myTable
    WHERE columnA > 1000

더 알아보기 (Learn more)