본문으로 건너뛰기
최종 수정: 에

역색인

요약 Apache Doris 역색인은 INDEX ... USING INVERTED로 어떤 컬럼에든 선언할 수 있는 CLucene 기반 보조 인덱스입니다. 행을 스캔하는 대신 포스팅 리스트를 읽어 동등 조건, 범위 조건, IN, IS NULL, 배열 원소 포함 여부, 전문 검색 MATCH_* 조건자의 평가를 가속합니다. Apache Doris 2.0부터 사용할 수 있습니다.

Apache Doris 역색인: 모든 컬럼 타입에서 동등 조건, 범위 조건, IN, IS NULL, 텍스트 조건자를 전체 스캔 대신 포스팅 리스트 조회로 처리하는 보조 인덱스입니다.

Apache Doris에서 역색인을 사용하는 이유는 무엇인가요?​

Apache Doris 역색인은 OLAP 쿼리에서 가장 큰 비용을 차지하는 WHERE 조건자 평가를 전체 스캔에서 포스팅 리스트 조회로 전환합니다. 2 TB 규모의 이벤트 테이블에서 user_id 조건, request_time 범위 조건, url 키워드 검색을 수행한다면, 역색인 없이는 필터링 후 남은 모든 세그먼트의 모든 행에 대해 각 컬럼을 디코딩해야 합니다. 일반적인 대안인 증분 구체화된 뷰나 별도 검색 클러스터는 스토리지와 운영 비용을 두 배로 늘립니다.

특히 다음과 같은 경우에 비용이 커집니다.

  • 카디널리티가 높은 컬럼의 동등 조건 조회(WHERE order_id = ?)는 적은 비용으로 세그먼트를 제외할 수단이 없어 태블릿 전체를 스캔합니다.
  • 숫자형 또는 날짜 및 시간 컬럼의 범위 필터(WHERE event_time BETWEEN ...)는 ZoneMap min/max로만 범위를 좁히므로, 여전히 해당 범위의 모든 페이지에 접근합니다.
  • 기본 SQL 엔진은 텍스트 컬럼의 키워드 검색을 지원하지 않습니다. LIKE '%term%'는 스캔으로 처리되며, 데이터를 Elasticsearch로 보내려면 동기화를 유지할 별도의 파이프라인이 필요합니다.

Apache Doris 역색인은 동일한 DDL로 이 세 가지를 모두 처리합니다.

Apache Doris 역색인이란 무엇인가요?​

Apache Doris 역색인은 각 값을, 텍스트 컬럼의 경우 토큰화된 각 텀을, 해당 값이나 텀을 포함하는 행 ID 집합에 매핑하는 컬럼 수준 보조 인덱스입니다. Lucene을 C++로 포팅한 CLucene을 기반으로 하며, Apache Doris의 컬럼형 스토리지 및 벡터화 실행과 완전히 통합되어 있습니다.

역색인은 DDL에서 INDEX <name>(<col>) USING INVERTED [PROPERTIES(...)]로 선언합니다. 지원되는 컬럼 타입에는 정수 계열, DECIMAL, DATE, DATETIME, IPV4, IPV6, CHAR/VARCHAR/STRING, 그리고 이러한 타입을 원소로 갖는 ARRAY<T>가 포함됩니다. 텍스트 컬럼에는 parser를 지정하여 토큰화를 제어할 수 있습니다.

주요 용어

  • Posting list: 값 또는 텀에 연결된 행 ID 집합입니다. 조건자 평가는 포스팅 리스트 조회와 집합 연산(AND/OR/NOT)으로 처리됩니다.
  • Parser: 텍스트 컬럼에 사용하는 토크나이저입니다. 기본 제공 옵션은 none(문자열 전체, 기본값), english, chinese, unicode, standard이며, 3.1부터는 사용자 정의 분석기도 지원합니다.
  • MATCH operators: MATCH_ANY(하나 이상의 텀), MATCH_ALL(모든 텀), MATCH_PHRASE(인접한 텀), MATCH_PHRASE_PREFIX, MATCH_REGEXP입니다. 이 연산자에는 파서가 설정된 역색인이 필요하지만, 일반적인 =와 LIKE에는 필요하지 않습니다.
  • .idx file: 디스크에 저장되는 인덱스 데이터입니다. 태블릿 디렉터리의 각 세그먼트 파일 옆에 .idx 파일이 하나씩 있으므로, 인덱스는 세그먼트에 종속되며 compaction 과정에서도 rowset과 함께 이동합니다.

Apache Doris 역색인은 어떻게 작동하나요?​

Apache Doris 역색인은 한 번 구축한 뒤 여러 번 조회하는 방식으로 작동하며, 인덱스를 해당 인덱스가 참조하는 세그먼트와 같은 위치에 저장합니다.

  1. 쓰기 시점에 구축합니다. rowset이 플러시되면 BE는 같은 디렉터리의 .idx 파일에 CLucene 형식의 인덱스를 기록합니다. 세그먼트의 각 행과 CLucene DocID는 1:1로 대응하므로, 별도의 행과 문서 간 매핑 테이블을 유지할 필요가 없습니다.
  2. 쿼리 시점에 경로를 선택합니다. 플래너가 푸시다운한 조건자에 대해 BE는 먼저 역색인을 조회합니다. 구축 시 사용한 것과 동일한 파서로 리터럴을 토큰화하고, 텀 사전을 탐색한 다음 포스팅 리스트를 읽습니다.
  3. 집합 연산으로 결합합니다. 조건자 사이의 AND, OR, NOT은 포스팅 리스트의 교집합, 합집합, 여집합 연산으로 처리되며, 이 과정에서 컬럼 데이터에는 접근하지 않습니다.
  4. 행 비트맵을 스캐너에 전달합니다. 스캐너는 컬럼형 스토리지에서 필터링 후 남은 행만 읽습니다. ZoneMap, BloomFilter, 역색인의 결과는 함께 적용되며, 각 단계가 다음 단계에서 처리해야 할 작업량을 줄입니다.
  5. 세그먼트 단위로 유지합니다. 인덱스가 세그먼트 옆에 저장되므로 compaction 시 데이터와 함께 병합되고, 데이터를 원격 스토리지 계층으로 옮길 때도 함께 이동합니다. 다시 구축해야 할 전역 인덱스가 없습니다.

빠른 시작​

CREATE TABLE access_log (
ts DATETIME NOT NULL,
user_id BIGINT NOT NULL,
status INT NOT NULL,
url STRING NOT NULL,
INDEX idx_user(user_id) USING INVERTED,
INDEX idx_status(status) USING INVERTED,
INDEX idx_url(url) USING INVERTED PROPERTIES("parser"="english")
) DUPLICATE KEY(ts) DISTRIBUTED BY HASH(user_id) BUCKETS 4;

INSERT INTO access_log VALUES
('2026-05-01 10:00:00', 42, 200, '/api/orders/list'),
('2026-05-01 10:01:00', 42, 500, '/api/orders/create'),
('2026-05-01 10:02:00', 99, 200, '/api/users/profile');

SELECT user_id, status, url FROM access_log
WHERE user_id = 42 AND status >= 500 AND url MATCH_ANY 'orders';

예상 결과

+---------+--------+---------------------+
| user_id | status | url |
+---------+--------+---------------------+
| 42 | 500 | /api/orders/create |
+---------+--------+---------------------+

각 조건자는 인덱스 조회로 처리됩니다. user_id = 42와 status >= 500은 역색인을 사용해 각각 동등 조건과 범위 조건을 처리합니다. url MATCH_ANY 'orders'는 english 파서로 컬럼을 토큰화하고 orders의 포스팅 리스트를 조회합니다. 스캐너는 세 포스팅 리스트의 AND 결과에 남은 행 하나만 읽습니다.

Apache Doris 역색인은 언제 사용해야 하나요?​

Apache Doris 역색인은 카디널리티가 높은 조건자, 텍스트 및 배열 검색, ZoneMap으로 충분히 세밀하게 데이터를 걸러내기 어려운 컬럼에서 효과적입니다.

적합한 경우

  • 키가 아닌 컬럼에 대한 카디널리티가 높은 동등 조건 및 IN 조건: order_id, trace_id, device_id.
  • 범위 및 BETWEEN 필터: 숫자형 또는 DATETIME 컬럼에서 ZoneMap만으로는 데이터를 충분히 세밀하게 걸러내기 어려운 경우.
  • 희소 컬럼에 대한 IS NULL / IS NOT NULL 조건. 인덱스는 NULL을 기록하고 O(1)에 조회합니다.
  • 로그, 문서, 콘텐츠 컬럼의 키워드 및 구문 검색. 연산자 목록은 전문 검색을, 관련도 순위는 BM25 점수 계산을 참조하세요.
  • 배열 원소 포함 여부 조건자: array_contains(tags, 'x') 및 array_overlaps(tags, ['a','b']).

적합하지 않은 경우

  • 앞에 와일드카드가 있는 LIKE '%abc%'. 역색인은 부분 문자열 LIKE를 가속하지 않습니다. 대신 NGram BloomFilter 인덱스를 사용하세요.
  • 테이블에서 필터링에 가장 자주 사용하는 컬럼. Key 컬럼으로 지정하여 접두사 인덱스로 처리하세요. 접두사 인덱스는 더 조밀하며 별도 비용이 없습니다.
  • 전체 스캔이 밀리초 만에 끝나는 작은 테이블(수백만 행 정도). 인덱스 조회 비용에 비해 건너뛰는 데이터 양이 훨씬 많을 때에만 인덱스를 사용하는 효과가 있습니다.
  • 조건자가 이미 대부분의 행과 일치하는 컬럼. 포스팅 리스트는 짧을 때 유리합니다. 행의 80%가 남는다면 어느 쪽이든 스캐너가 수행하는 작업량은 같습니다.
  • 스토리지 용량이 제한된 테이블. 데이터 크기가 큰 텍스트 컬럼의 역색인은 크기가 컬럼 자체에 맞먹을 수 있습니다. 텍스트 검색보다 저장 공간이 더 중요하다면 BloomFilter 인덱스를 사용하세요.

더 읽어보기​