- Table Type Should You Use
- INDEX - BITMAP
- INDEX- Boolm
- Partitioning
- Bucketing
- Ordering
- ETC
- Set data compression algorithm
- Forbid Base Compaction
Table Type Should You Use
- 중복 키 테이블(Duplicated Key Table): 상세 기록을 유지해야 할 때 사용하세요(팩트 테이블).
- 프라이머리 키 테이블(Primary Key Table): 다음 조건에 해당할 때 사용하세요:
- NULL이 아닌 고유 컬럼이 있는 경우
- 읽기와 쓰기가 빈번한 경우
- 프라이머리 키가 아닌 컬럼이 인덱스를 활용하는 경우
- 유니크 키 테이블(Unique Key Table): 다음 조건에 해당할 때 사용하세요:
- NULL 값이 허용되는 고유 컬럼이 있는 경우
- 쓰기는 빈번하지만 읽기는 드문 경우
- 집계 테이블(Aggregate Table): 집계된 데이터만 유지하는 경우 사용하세요.
INDEX - BITMAP
기본 개념
비트맵 인덱스는 0과 1의 값만 가지는 비트 배열을 사용하는 특수한 데이터베이스 인덱스입니다. 각 비트는 테이블의 한 행(row)에 대응하며, 그 값은 해당 행의 값에 따라 결정됩니다.
성능 향상 원리
비트맵 인덱스는 특정 컬럼에 대한 쿼리 성능을 향상시킬 수 있습니다. 프리픽스 인덱스(prefix index)는 쿼리의 필터 조건이 일치할 때 큰 성능 향상을 가져오지만, 테이블당 하나만 가질 수 있다는 제한이 있습니다. 비트맵 인덱스는 프리픽스 인덱스를 활용할 수 없는 상황에서 대안으로 사용할 수 있습니다.
효과적인 비트맵 인덱스 설계 방법
비트맵 인덱스 설계 시 고려할 주요 사항:
- 컬럼 카디널리티(고유값의 수): 일반적인 생각과 달리, StarRocks의 비트맵 인덱스는 높은 카디널리티를 가진 컬럼이나 낮은 카디널리티를 가진 여러 컬럼의 조합에 더 적합합니다.
- 필터링 효과: 효과적인 비트맵 인덱스는 최소 999/1000의 데이터를 필터링하여 읽어야 할 페이지 데이터의 양을 줄여야 합니다.
- 데이터 로딩 비용 고려: 데이터는 페이지(기본 64K) 단위로 구성되고 로드됩니다. 디스크에서 페이지 로딩, 압축 해제, 디코딩 시간을 고려해야 합니다.
- 카디널리티 균형: 지나치게 높은 카디널리티는 디스크 공간을 많이 차지하고 데이터 로딩 중 비트맵 인덱스 구성에 영향을 줄 수 있습니다.
적응형 선택 메커니즘
StarRocks는 비트맵 인덱스의 사용 여부를 자동으로 결정합니다:
- 결정 기준: 쿼리 조건에 포함된 값의 수와 컬럼 카디널리티의 비율이 임계값보다 작을 때 비트맵 인덱스가 사용됩니다.
- 임계값: 기본적으로 1/1000로 설정된
bitmap_max_filter_ratio를 사용합니다. - 예시: 성별(gender) 컬럼의 카디널리티가 2(남성, 여성)이고 쿼리가 하나의 값(남성)을 포함한다면 비율은 1/2로, 1/1000보다 크므로 비트맵 인덱스는 사용되지 않습니다.
- 복합 예시: 성별 필터와 도시 필터가 함께 사용되고, 도시 컬럼의 카디널리티가 10,000이라면, 비율 계산 결과가 1/1000보다 작아 비트맵 인덱스가 사용됩니다.
장점
- 쿼리된 컬럼 값의 행 번호를 빠르게 찾아내 포인트 쿼리나 소규모 범위 쿼리에 적합
- 복수 조건을 결합하는 다차원 쿼리(OR, AND 연산)에 최적화 가능
적합한 쿼리 유형
비트맵 인덱스는 다음과 같은 쿼리에 적합합니다:
- 동등 쿼리(=)
- 범위 쿼리(>, >=, <, <=)
- [NOT] IN 쿼리
- IS NULL 쿼리
- [NOT] LIKE 쿼리
그러나 !=와 같은 부정 쿼리에는 적합하지 않습니다.
INDEX- Boolm
기본 개념
블룸 필터 인덱스는 테이블의 데이터 파일에서 필터링 대상 데이터의 존재 가능성을 감지하는 공간 효율적 자료구조입니다. 이 인덱스가 특정 데이터 파일에 필터링할 데이터가 없다고 감지하면, StarRocks는 해당 데이터 파일의 스캔을 건너뛰어 쿼리 성능을 향상시킵니다. 특히 고유값이 많은(카디널리티가 높은) ID와 같은 컬럼에서 응답 시간을 단축시킬 수 있습니다.
사용 시나리오
쿼리가 정렬 키 컬럼(sort key column)을 사용할 경우, StarRocks는 프리픽스 인덱스를 활용해 효율적으로 결과를 반환합니다. 그러나 프리픽스 인덱스 항목은 데이터 블록당 36바이트를 초과할 수 없다는 제한이 있습니다. 정렬 키로 사용되지 않으면서 카디널리티가 높은 컬럼의 쿼리 성능을 개선하고 싶을 때 블룸 필터 인덱스를 생성할 수 있습니다.
작동 원리
예를 들어, table1 테이블의 column1에 블룸 필터 인덱스를 생성하고 Select xxx from table1 where column1 = something;과 같은 쿼리를 실행한다고 가정해보겠습니다. 이때 StarRocks가 table1의 데이터 파일을 스캔할 때 다음과 같은 상황이 발생합니다:
- 데이터 파일 스킵: 블룸 필터 인덱스가 데이터 파일에 필터링할 데이터가 없다고 감지하면, StarRocks는 해당 파일을 건너뛰어 쿼리 성능을 향상시킵니다.
- 존재 가능성 감지: 블룸 필터 인덱스가 데이터 파일에 필터링할 데이터가 존재할 수 있다고 감지하면, StarRocks는 데이터 파일을 읽어 실제 데이터의 존재 여부를 확인합니다. 중요: 블룸 필터는 값이 존재하지 않는다는 것은 100% 확신할 수 있지만, 값이 존재한다는 것은 '존재할 수 있다'는 가능성만 제시할 뿐입니다. 이로 인해 '거짓 양성(false positive)'이 발생할 수 있습니다 - 즉, 블룸 필터 인덱스가 데이터 파일에 필터링할 데이터가 있다고 감지했지만, 실제로는 데이터가 없는 경우입니다.
사용 시 고려사항
- 적용 가능한 테이블 유형:
- Duplicate Key 또는 Primary Key 테이블의 모든 컬럼에 블룸 필터 인덱스를 생성할 수 있습니다.
- Aggregate 테이블이나 Unique Key 테이블의 경우, 키 컬럼에만 블룸 필터 인덱스를 생성할 수 있습니다.
- 지원되는 데이터 타입:
- 숫자 타입: SMALLINT, INT, BIGINT, LARGEINT
- 문자열 타입: CHAR, STRING, VARCHAR
- 날짜 타입: DATE, DATETIME
- 최적화되는 쿼리 유형:
- 블룸 필터 인덱스는
in과=연산자를 포함하는 쿼리의 성능만 향상시킬 수 있습니다. - 예시:
Select xxx from table where x in {}나Select xxx from table where column = xxx - 사용 여부 확인:
- 쿼리가 블룸 필터 인덱스를 사용하는지는 해당 쿼리의 프로파일에서
BloomFilterFilterRows필드를 확인하여 알 수 있습니다.
PROPERTIES (
"bloom_filter_columns"="k1,k2,k3"
)Partitioning
파티셔닝 방법 | 적합한 시나리오 | 파티션 생성 방법 |
표현식 파티셔닝 (권장) | 이전에는 자동 파티셔닝이라고 불렸습니다. 이 파티셔닝 방법은 더 유연하고 사용하기 쉽습니다. 연속적인 날짜 범위나 열거형(enum) 값을 기준으로 데이터를 조회하고 관리하는 대부분의 시나리오에 적합합니다. | 데이터 로딩 중 자동으로 생성됨 |
범위 파티셔닝 | 주로 연속적인 날짜/숫자 범위를 기준으로 자주 조회하고 관리하는 단순하고 순서가 있는 데이터를 저장하는 시나리오에 적합합니다. 예를 들어, 특수한 경우에는 과거 데이터는 월별로 파티셔닝하고 최근 데이터는 일별로 파티셔닝해야 할 수 있습니다. | 수동, 동적 또는 일괄 생성 |
목록 파티셔닝 | 열거형 값을 기준으로 데이터를 조회하고 관리하는 전형적인 시나리오에 적합하며, 하나의 파티션이 각 파티셔닝 컬럼에 대해 다른 값을 가진 데이터를 포함해야 하는 경우입니다. 예를 들어, 국가와 도시를 기준으로 데이터를 자주 조회하고 관리하는 경우 이 방법을 사용하고 city를 파티셔닝 컬럼으로 선택할 수 있습니다. 따라서 하나의 파티션은 같은 국가에 속하는 여러 도시의 데이터를 저장할 수 있습니다. | 수동으로 생성 |
- 권장: 파티션 생성을 위한 WHERE 필터링에 변경되지 않는 시간 컬럼을 사용하세요.
- 권장: 데이터 만료 요구사항이 있는 시나리오에서는 동적 파티션을 선택하세요.
- 필수: 뚜렷한 핫 데이터와 콜드 데이터 특성이 있는 데이터에 대해 파티션을 생성하세요. 예를 들어, 지난 주에 자주 업데이트되는 데이터는 일별로 파티션할 수 있습니다.
- 필수: 각 파티션은 100GB를 초과하지 않아야 합니다.
- 필수: 50GB 또는 500만 행을 초과하는 테이블은 반드시 파티션을 가져야 합니다.
- 권장: 필요에 따라 파티션을 생성하세요. 메타데이터로 인한 과도한 FE(Frontend) 메모리 사용을 방지하기 위해 다수의 빈 파티션 생성을 피하세요.
- 현재 시간(범위, 표현식 파티션), 문자열(리스트 파티션), 그리고 숫자(범위, 리스트 파티션) 유형을 지원합니다.
- 기본적으로 최대 1,024개의 파티션이 지원되며, 설정을 통해 조정 가능합니다. 하지만 일반적으로 이러한 조정은 불필요합니다.
example - Range partitioning
Bucketing
- 필수: 프로덕션 환경에서는 반드시 3개의 복제본을 사용해야 합니다.
- 버킷 수 결정:
- 필수: 각 버킷을 1GB로 추정하고, 원시 데이터는 10GB로 추정합니다(압축 비율 7:1에서 10:1). 계산된 버킷 수가 BE(Backend) 노드 수보다 적은 경우, 최종 버킷 수는 BE 노드 수와 일치해야 합니다. 예를 들어, 6개의 BE 노드가 있다면 최소 6개의 버킷이 필요합니다.
- 필수: 파티션이 없는 테이블에는 동적 버킷팅을 사용하지 마세요. 실제 데이터 볼륨을 기준으로 버킷 수를 추정하세요.
- 필수: 파티션된 테이블 내의 파티션들이 데이터 크기에서 크게 차이가 나는 경우 동적 버킷팅을 피하세요.
- 버킷팅과 데이터 스큐 방지 방법:
- 권장: 버킷팅 컬럼이 WHERE 조건에서 자주 사용되고 중복이 적은 경우(예: 사용자 ID, 트랜잭션 ID), 해당 컬럼을 버킷팅 컬럼으로 사용할 수 있습니다.
- 권장: 쿼리에 자주
city_id와site_id가 함께 포함되고,city_id가 낮은 카디널리티(고유값이 적음)를 가진 경우,city_id만 버킷팅 컬럼으로 사용하면 상당한 데이터 스큐(불균형)가 발생할 수 있습니다. 이 경우,city_id와site_id를 결합하여 버킷팅 필드로 사용하는 것을 고려하세요. 그러나 이 방식은 쿼리에city_id만 포함된 경우 버킷팅 이점이 감소할 수 있습니다. - 권장: 데이터를 분산시키기 위한 적절한 필드가 없는 경우, 랜덤 버킷팅을 고려하세요. 단, 이 방식은 버킷 트리밍(불필요한 버킷 제외)의 이점을 제거합니다.
- 필수: 각각 천 개 이상의 행을 가진 두 개 이상의 테이블이 조인되는 경우, Colocate 조인(동일 위치 조인) 사용을 고려하세요.
Ordering
비즈니스 시나리오에서의 쿼리와 데이터 분석은 적절한 정렬 키 컬럼을 선택하고 이를 올바른 순서로 배치하여 Prefix 인덱스를 형성하는 데 도움을 주며, 이는 쿼리 성능을 크게 향상시킬 수 있습니다.
- 정렬 키 컬럼의 수는 일반적으로 3개이며, 4개를 초과하지 않는 것이 좋습니다. 너무 많은 컬럼을 가진 정렬 키는 쿼리 성능을 향상시키지 못하고 데이터 로딩 중 정렬 오버헤드만 증가시킵니다.
- 아래 순서대로 컬럼의 우선순위를 정하여 정렬 키를 구성하는 것을 권장합니다:
- 쿼리 필터 조건에서 자주 사용되는 컬럼을 정렬 키 컬럼으로 선택하세요. 정렬 키 컬럼이 여러 개인 경우, 쿼리 필터 조건에서의 사용 빈도에 따라 내림차순으로 배열하세요. 이렇게 하면 쿼리 필터 조건이 Prefix 인덱스의 접두사를 포함할 경우 쿼리 성능이 크게 향상될 수 있습니다. 그리고 필터 조건이 Prefix 인덱스의 전체 접두사를 포함하는 경우, 쿼리는 Prefix 인덱스를 완전히 활용할 수 있습니다. 물론 필터 조건이 접두사를 포함하지만 전체 접두사가 아닌 경우에도 Prefix 인덱스는 여전히 쿼리를 최적화할 수 있습니다. 그러나 필터 조건에 포함된 접두사의 길이가 너무 짧으면 Prefix 인덱스의 효과가 약화됩니다. 예를 들어, 정렬 키가
(uid,name)인 Unique Key 테이블을 예로 들면, 쿼리 필터 조건이 전체 접두사를 포함하는 경우(select sum(credits) from user_access where uid = 123 and name = 'Jane Smith';) 쿼리는 Prefix 인덱스를 완전히 활용하여 성능을 향상시킬 수 있습니다. 쿼리 조건에 접두사의 일부만 포함된 경우(select sum(credits) from user_access where uid = 123;)에도 쿼리는 Prefix 인덱스를 활용하여 성능을 향상시킬 수 있습니다. 그러나 쿼리 조건이 접두사를 포함하지 않는 경우(select sum(credits) from user_access where name = 'Jane Smith';)에는 쿼리가 Prefix 인덱스를 사용하여 가속화할 수 없습니다. - 여러 정렬 키 컬럼이 쿼리 필터 조건으로서 유사한 빈도를 가진 경우, 이러한 컬럼의 카디널리티(고유값의 수)를 측정할 수 있습니다.
- 컬럼의 카디널리티가 높으면 쿼리 중에 더 많은 데이터를 필터링할 수 있습니다. 카디널리티가 너무 낮은 경우(예: 불리언 타입 컬럼)에는 필터링 효과가 이상적이지 않습니다.
Tip: 그러나 실제 비즈니스 시나리오에서의 쿼리 특성을 고려할 때, 일반적으로 카디널리티가 약간 낮은 컬럼이 카디널리티가 높은 컬럼보다 쿼리 조건으로 더 자주 사용됩니다. 이는 필터링이 높은 카디널리티 컬럼을 기반으로 하는 쿼리나, 심지어 UNIQUE 제약 조건이 있는 컬럼을 기반으로 하는 일부 극단적인 시나리오에서는 OLAP 데이터베이스의 복잡한 분석 쿼리보다는 OLTP 데이터베이스의 포인트 쿼리에 더 가깝기 때문입니다.
- 또한 저장소 압축 요소도 고려하세요. 낮은 카디널리티 컬럼과 높은 카디널리티 컬럼의 순서에 따른 쿼리 성능 차이가 뚜렷하지 않은 경우, 낮은 카디널리티 컬럼을 높은 카디널리티 컬럼 앞에 배치하면 정렬된 낮은 카디널리티 컬럼에 대해 훨씬 높은 저장소 압축률을 얻을 수 있습니다. 따라서 낮은카디널리티 컬럼을 앞에 배치하는 것이 좋습니다.
ETC
Set data compression algorithm
테이블 생성 시 compression 속성을 추가하여 데이터 압축 알고리즘을 지정할 수 있습니다.
compression의 유효한 값은 다음과 같습니다:
LZ4: LZ4 알고리즘 - 빠른 압축 및 압축 해제 속도가 특징인 경량 압축 알고리즘입니다.ZSTD: Zstandard 알고리즘 - 우수한 압축률과 빠른 속도의 균형을 제공하는 현대적인 압축 알고리즘입니다.ZLIB: zlib 알고리즘 - 오랫동안 사용된 표준 압축 알고리즘으로 안정성이 뛰어납니다.SNAPPY: Snappy 알고리즘 - Google에서 개발한 압축률보다 속도에 중점을 둔 압축 알고리즘입니다.
PROPERTIES ("compression" = "zstd(<compression_level>)") # LZ4 is default