TimeSeries 테이블 엔진

TimeSeries 테이블 엔진

타임스탬프와 태그(또는 라벨)와 연관된 값들의 집합, 즉 시계열(time series)을 저장하는 테이블 엔진이에요.

metric_name1[tag1=value1, tag2=value2, ...] = {timestamp1: value1, timestamp2: value2, ...}
metric_name2[...] = ...

이것은 프라이빗 프리뷰 기능으로, 향후 릴리스에서 이전 버전과 호환되지 않는 방식으로 변경될 수 있어요. TimeSeries 테이블 엔진 사용은 enable_time_series_table 설정으로 활성화해요. set enable_time_series_table = 1 명령을 입력해요.

TimeSeries 테이블 엔진은 ClickHouse Cloud에서 프라이빗 프리뷰 기능으로 사용할 수 있어요. 프라이빗 프리뷰에 참여하는 서비스에는 이미 enable_time_series_table 설정이 구성되어 있어요. 다른 ClickHouse Cloud 서비스에는 이 구성이 없으므로 그런 서비스에서 직접 엔진을 활성화할 수 없어요.

출처: 문서

본문

문법

CREATE TABLE name [(columns)] ENGINE=TimeSeries
[SETTINGS var1=value1, ...]
[SAMPLES db.samples_table_name | [SAMPLES INNER COLUMNS (...)] [SAMPLES INNER ENGINE engine(arguments)]]
[RECENT SAMPLES db.recent_samples_table_name | [RECENT SAMPLES INNER COLUMNS (...)] [RECENT SAMPLES INNER ENGINE engine(arguments)]]
[TAGS db.tags_table_name | [TAGS INNER COLUMNS (...)] [TAGS INNER ENGINE engine(arguments)]]
[METRIC FAMILIES db.metric_families_table_name | [METRIC FAMILIES INNER COLUMNS (...)] [METRIC FAMILIES INNER ENGINE engine(arguments)]]

키워드 SAMPLES에는 별칭 DATA가 있고, 키워드 METRIC FAMILIES에는 별칭 METRICS가 있으며, 둘 다 이전 버전과의 호환성을 위해 유지돼요. 버전 4 이전 테이블의 정의는 METRICS로 작성되어 이전 서버가 읽을 수 있어요.

사용법

모든 것을 기본값으로 설정하고 시작하는 것이 더 쉬워요 (컬럼 목록을 지정하지 않고 TimeSeries 테이블을 만드는 것이 허용돼요):

CREATE TABLE my_table ENGINE=TimeSeries

그런 다음 이 테이블은 다음 프로토콜과 함께 사용할 수 있어요 (서버 구성에서 포트가 할당되어야 함):

외부 컬럼 (Outer columns)

TimeSeries 테이블의 컬럼은 자동으로 생성돼요. 이들은 외부 컬럼으로, 데이터를 저장하지 않고 SELECT/INSERT를 위한 인터페이스만 제공해요. 실제 데이터는 target tables에 저장돼요. 다음은 외부 컬럼 목록이에요.

Name Type Description
metric_name String 메트릭의 이름
tags Map(String, String) 시계열의 태그(라벨) 맵
samples Array(Tuple(DateTime64(3), Float64)) (기본) 시계열의 (timestamp, value) 쌍 배열. 튜플의 타임스탬프와 스칼라 요소 타입은 samples INNER COLUMNS 선언에서 파생될 수 있어요 (Specifying outer columns 참고). 컬럼은 버전 2 이하의 테이블에서 time_series로 명명돼요
metric_family String 메트릭 패밀리 이름 (메트릭 메타데이터용)
type String 메트릭 유형 (예: "counter", "gauge")
unit String 메트릭의 단위
help String 메트릭 설명

예시:

INSERT INTO my_table (metric_name, tags, samples) VALUES
    ('cpu_usage', {'job': 'node_exporter', 'instance': 'host1:9100'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5), (toDateTime64('2024-01-01 00:01:00', 3), 0.7)])

metric_name은 삽입 시 비어 있어도 허용돼요. 이는 메트릭 이름이 tags__name__ 아래 지정된다는 뜻이에요. 예:

INSERT INTO my_table (tags, samples) VALUES
    ({'__name__': 'cpu_usage', 'job': 'test'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5)])

메트릭 메타데이터를 삽입하려면 metric_family, type, unit, help 컬럼에 삽입해요.

INSERT INTO my_table (metric_name, tags, samples, metric_family, type, unit, help) VALUES
    ('http_requests_total', {'method': 'GET'}, [(now64(), 100.0)],
     'http_requests_total', 'counter', 'requests', 'Total HTTP requests')

외부 컬럼 지정하기

외부 samples 컬럼은 CREATE TABLE 문에서 명시적으로 나열하여 기본 Array(Tuple(DateTime64(3), Float64)) 타입을 재정의할 수 있어요 (옛 이름 time_series도 허용돼요). ClickHouse는 튜플에서 타임스탬프와 스칼라 타입을 추출해 내부 samples 테이블로 전파해요.

CREATE TABLE my_table (samples Array(Tuple(UInt32, Float32))) ENGINE=TimeSeries

이것은 samples INNER COLUMNS 절에서 타임스탬프와 값 컬럼 타입을 직접 선언하는 것과 동일해요.

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp UInt32 CODEC(Delta, T64, ZSTD(3)), value Float32 CODEC(ALP, ZSTD(3)))

같은 CREATE TABLE 문에서 두 형식을 모두 사용하면 선언된 타입이 일치해야 해요.

Target tables

TimeSeries 테이블은 자체 데이터가 없고 모든 것이 target tables에 저장돼요. 이것은 materialized view가 동작하는 방식과 비슷한데, 차이점은 materialized view는 target table이 하나인 반면 TimeSeries 테이블은 samples, tags, metric families라는 세 개의 필수 target table과, 기본적으로 활성화된 선택적 recent samples target table( recent_samples_ttl_seconds 설정 참고)이 있다는 점이에요.

target table은 CREATE TABLE 쿼리에서 명시적으로 지정하거나 TimeSeries 테이블 엔진이 내부 target table을 자동으로 생성할 수 있어요. TimeSeries 테이블에 삽입된 행은 변환되어 블록으로 나뉘고 이 target table들에 삽입돼요. target table은 다음과 같아요.

Samples 테이블

samples 테이블은 일부 식별자와 연관된 시계열을 포함해요. samples 테이블은 다음 컬럼을 가져야 해요.

Name Mandatory? Default type Possible types Description
id [x] Tuple(UInt64, LowCardinality(UUID)) any 메트릭 이름과 태그의 조합을 식별
timestamp [x] DateTime64(3) DateTime64(X) 시점
value [x] Float64 Float32 or Float64 timestamp와 연관된 값

엔진이 스스로 만드는 컬럼은 시계열 압축 코덱을 얻어요: timestamp CODEC(Delta, T64, ZSTD(3))value CODEC(ALP, ZSTD(3)). 거의 단조적인(monotonic) 타임스탬프는 일반 코덱으로 거의 압축되지 않으며 그렇지 않으면 samples 테이블의 디스크 크기를 지배할 수 있어요. 엔진은 enable_alp_codec를 설정하지 않아도 내부 samples와 recent samples 테이블에 ALP를 활성화해요. Adjusting types of columns도 참고해요.

Recent samples 테이블

recent samples 테이블은 선택 사항이며 기본적으로 활성화돼요 (recent_samples_ttl_seconds 설정 참고; 0으로 설정하면 테이블이 비활성화됨). 이 설정으로 정의된 TTL보다 새로운 samples의 복사본을 포함하며, samples 테이블과 같은 컬럼을 가져야 해요. 생성된 timestamp 컬럼은 CODEC(Delta, T64, ZSTD(3))을 사용하고, 생성된 value 컬럼은 CODEC(ALP, ZSTD(3))을 사용해요.

모든 삽입된 sample은 samples 테이블과 recent samples 테이블 둘 다에 쓰여져요. 시간 범위가 TTL 창에 맞는 쿼리는 기본 samples 테이블 대신 recent samples 테이블에서 읽어요. 훨씬 작기 때문이에요 (이것은 쿼리 레벨 설정 time_series_prefer_recent_samples_table로 비활성화할 수 있어요). 내부 recent samples 테이블의 TTL은 항상 recent_samples_ttl_seconds 설정에서 파생돼요.

Tags 테이블

tags 테이블은 메트릭 이름과 태그의 각 조합에 대해 계산된 식별자를 포함해요. tags 테이블은 다음 컬럼을 가져야 해요.

Name Mandatory? Default type Possible types Description
id [x] Tuple(UInt64, LowCardinality(UUID)) any (samples 테이블의 id 타입과 일치해야 함) id는 메트릭 이름과 태그의 조합을 식별. DEFAULT 표현식은 그러한 식별자를 계산하는 방법을 지정
metric_name [x] LowCardinality(String) String or LowCardinality(String) 메트릭 이름
<tag_value_column> [ ] String String or LowCardinality(String) or LowCardinality(Nullable(String)) 특정 태그의 값. 태그 이름과 해당 컬럼 이름은 tags_to_columns 설정에 지정
tags [x] Map(LowCardinality(String), String) Map(String, String) or Map(LowCardinality(String), String) or Map(LowCardinality(String), LowCardinality(String)) 모든 태그의 맵. 메트릭 이름을 포함하는 태그 __name__tags_to_columns 설정에 열거된 이름의 태그를 포함. ClickHouse의 구 버전이 만든 테이블은 이 컬럼에 전용 컬럼과 메트릭 이름이 없는 태그만 저장했으며, 읽기는 두 경우 모두 처리함
min_time [ ] Nullable(DateTime64(3)) DateTime64(X) or Nullable(DateTime64(X)) 해당 id를 가진 시계열의 최소 타임스탬프. store_min_time_and_max_timetrue면 컬럼이 생성됨
max_time [ ] Nullable(DateTime64(3)) DateTime64(X) or Nullable(DateTime64(X)) 해당 id를 가진 시계열의 최대 타임스탬프. store_min_time_and_max_timetrue면 컬럼이 생성됨

버전 5 이상의 새 내부 tags 테이블은 MergeTree 패밀리 엔진을 사용하면 tags에 역 텍스트 인덱스가 있어요: INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs'). 이것은 PromQL에서 {job="api"} 같은 정확한 라벨 일치를 키와 값을 함께 조회해 가속화해요. 빈 문자열과의 비교도 누락된 라벨과 일치하지만 이 인덱스를 사용하지 않아요.

TAGS INNER COLUMNS에 선언된 명시적 인덱스는 기본 인덱스를 대체해요. 기존 테이블과 외부 tags 테이블은 자체 인덱스를 유지해요. 인덱스를 활성화하려면 tags target table에 인덱스를 추가하고 materialize해요.

Metric families 테이블

metric families 테이블은 수집 중인 메트릭 패밀리에 대한 정보, 해당 메트릭 패밀리의 유형과 설명을 포함해요. 메트릭 패밀리는 같은 이름(__name__ 태그)과 같은 유형을 가진 메트릭 그룹이에요. 예를 들어 히스토그램은 여러 메트릭으로 구성된 metric family예요. metric families 테이블은 다음 컬럼을 가져야 해요.

Name Mandatory? Default type Possible types Description
metric_family_name [x] String String or LowCardinality(String) 메트릭 패밀리 이름
type [x] LowCardinality(String) String or LowCardinality(String) 메트릭 패밀리 유형, "counter", "gauge", "summary", "stateset", "histogram", "gaugehistogram" 중 하나
unit [x] LowCardinality(String) String or LowCardinality(String) 메트릭에 사용된 단위
help [x] String String or LowCardinality(String) 메트릭 설명

생성

TimeSeries 테이블 엔진으로 테이블을 만드는 방법은 여러 가지가 있어요.

가장 단순한 문

CREATE TABLE my_table ENGINE=TimeSeries

은 실제로 다음 테이블을 만들게 돼요 (SHOW CREATE TABLE my_table을 실행해 볼 수 있음):

CREATE TABLE my_table
(
    `metric_name` String,
    `tags` Map(String, String),
    `samples` Array(Tuple(DateTime64(3), Float64)),
    `metric_family` String,
    `type` String,
    `unit` String,
    `help` String
)
ENGINE = TimeSeries
SETTINGS version = 5, recent_samples_ttl_seconds = 345600
SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, LowCardinality(UUID)),
    `timestamp` DateTime64(3) CODEC(Delta, T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
SAMPLES INNER ENGINE = MergeTree ORDER BY (id, timestamp) SETTINGS index_granularity = 32768
RECENT SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(Delta, T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
RECENT SAMPLES INNER ENGINE = MergeTree PARTITION BY toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) ORDER BY (id, timestamp) TTL toDateTime(timestamp) + toIntervalSecond(345600) SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
TAGS INNER COLUMNS
(
    `id` Tuple(UInt64, LowCardinality(UUID)) DEFAULT tuple(sipHash64(metric_name), toLowCardinality(reinterpretAsUUID(sipHash128(tags)))),
    `metric_name` LowCardinality(String),
    `tags` Map(LowCardinality(String), String),
    `min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
    `max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
    INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs') GRANULARITY 100000000
)
TAGS INNER ENGINE = AggregatingMergeTree PRIMARY KEY metric_name ORDER BY (metric_name, id) SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
METRIC FAMILIES INNER COLUMNS
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
METRIC FAMILIES INNER ENGINE = ReplacingMergeTree ORDER BY metric_family_name

따라서 컬럼이 자동으로 생성됐고 INNER COLUMNS 절에 자체 컬럼 정의가 있는 네 개의 내부 target table도 있어요. recent_samples_ttl_seconds 설정은 기본값과 함께 SETTINGS 절에 기록됐어요. 이 설정은 recent samples 테이블의 TTL을 정의하므로 생성 시 그 유효 값이 고정돼요. 또한 최신 스키마 버전이 version 설정에 고정됐어요 (Schema versioning 참고).

내부 target table은 .inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.recentsamples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.metricfamilies.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 같은 이름을 가지며 각 target table은 자체 컬럼 집합을 가져요.

CREATE TABLE default.`.inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, LowCardinality(UUID)),
    `timestamp` DateTime64(3) CODEC(Delta(8), T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
ENGINE = MergeTree
ORDER BY (id, timestamp)
SETTINGS index_granularity = 32768
CREATE TABLE default.`.inner_id.recentsamples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(Delta(8), T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
ENGINE = MergeTree
PARTITION BY toStartOfInterval(toDateTime(timestamp), toIntervalHour(5))
ORDER BY (id, timestamp)
TTL toDateTime(timestamp) + toIntervalSecond(345600)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
CREATE TABLE default.`.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, LowCardinality(UUID)) DEFAULT tuple(sipHash64(metric_name), toLowCardinality(reinterpretAsUUID(sipHash128(tags)))),
    `metric_name` LowCardinality(String),
    `tags` Map(LowCardinality(String), String),
    `min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
    `max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
    INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs') GRANULARITY 100000000
)
ENGINE = AggregatingMergeTree
PRIMARY KEY metric_name
ORDER BY (metric_name, id)
SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
CREATE TABLE default.`.inner_id.metricfamilies.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
ENGINE = ReplacingMergeTree
ORDER BY metric_family_name
SETTINGS index_granularity = 8192

기존 테이블 AS로 테이블 생성하기

CREATE TABLE new_table AS existing_table 문은 existing_table처럼 구성된 TimeSeries 테이블을 만들어요. existing_tableTimeSeries 테이블이어야 해요. existing_table의 외부 target은 복사되지 않아요: 문이 그 target을 직접 선언해야 해요.

문은 existing_table에서 다음을 복사해요.

  • SETTINGS 절 (version 제외): 새 테이블은 항상 최신 버전을 얻어요. 문에 쓰인 설정은 복사된 것과 이름으로 병합되므로, 쓰인 설정이 복사된 것보다 우선하고 name = DEFAULT는 복사된 설정을 기본값으로 재설정해요
  • 각 내부 테이블의 INNER COLUMNSINNER ENGINE 절. 커스텀 컬럼(예: 추가 컬럼, 코덱이나 DEFAULT 표현식이 있는 컬럼)과 커스텀 엔진 부분(예: 인자가 있는 엔진, 커스텀 정렬 키 또는 엔진 설정)은 유지되고, 다른 컬럼과 엔진 부분은 새 테이블의 설정에 맞춰 조정돼요. 그래서 예를 들어 문에 쓰인 tags_to_columns, aggregate_min_time_and_max_time, tags_index_granularity가 효과를 내요.

id 컬럼, 타임스탬프와 값 컬럼의 타입, 그리고 내부 엔진의 복제 유형(MergeTree, ReplicatedMergeTree 또는 SharedMergeTree)도 문이 직접 선언하지 않는 한 existing_table에서 가져와요. 외부 컬럼 목록은 재생성되고 복사되지 않아요.

ClickHouse의 구 버전이 만든 테이블을 existing_table로 사용할 수 있어요: 새 테이블은 현재 구조(예: 현재 id 타입과 기본 식별자 표현식)를 얻어요.

컬럼 타입 조정

INNER COLUMNS 절을 사용해 내부 target table의 컬럼 타입을 조정할 수 있어요. 예를 들어 타임스탬프를 마이크로초로, 값을 Float32로 저장하려면 다음을 사용해요.

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6) CODEC(Delta, T64, ZSTD(3)), value Float32 CODEC(ALP, ZSTD(3)))

코덱 없이 내부 컬럼을 지정한다는 것은 그들에 대해 기본 코덱을 사용한다는 뜻이에요.

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6), value Float32)

id 컬럼

id 컬럼은 식별자를 포함하고, 각 식별자는 메트릭 이름과 태그의 조합에 대해 계산돼요. 식별자를 생성하는 데 사용되는 타입과 DEFAULT 표현식은 TAGS INNER COLUMNS 절로 커스터마이즈할 수 있어요.

CREATE TABLE my_table ENGINE=TimeSeries
TAGS INNER COLUMNS (id UInt64 DEFAULT sipHash64(tags))

id 컬럼은 어떤 비교 가능한 비-Nullable 타입이든 될 수 있어요. samples와 tags 내부 테이블에 선언된 id 타입은 일치해야 해요.

id 컬럼에 DEFAULT 표현식이 주어지지 않고 id_generator 설정도 설정되지 않으면, ClickHouse는 id 타입에 기반해 DEFAULT 표현식을 자동으로 선택해요. 단 id 타입이 UUID, UInt64, UInt128, FixedString(16) 중 하나이거나, LowCardinality로 감싸진 같은 타입이거나, 그 두 타입의 튜플인 경우에만요. 그런 튜플의 경우 자동 선택된 표현식은 첫 구성 요소에서 메트릭 이름의 해시를, 두 번째 구성 요소에서 모든 태그의 해시를 계산해요.

LowCardinality 식별자 타입(예: Tuple(UInt64, LowCardinality(UUID)))은 식별자를 딕셔너리 인코딩으로 유지해요: samples 테이블은 모든 행에서 전체 식별자를 반복하는 대신 작은 per-block 딕셔너리와 딕셔너리 인덱스를 저장하므로 쿼리가 읽는 데이터 양이 줄어들어요.

id_generator 설정은 INNER COLUMNS 절을 사용하지 않고 같은 커스터마이즈를 제공해요.

CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_generator = 'sipHash64(tags)'

설정이 설정되면, 컬럼의 DEFAULT가 다른 표현식을 포함하더라도 id를 생성하는 데 사용돼요. id 컬럼의 타입은 INNER COLUMNS 절 대신 id_type 설정으로도 지정할 수 있어요.

CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_type = 'UInt64', id_generator = 'sipHash64(tags)'

id_generator 설정이 설정되면 id_type 설정은 CREATE 시 자동으로 기록돼요. 그래서 정의는 표현식이 작성된 타입을 유지해요.

tags 컬럼

tags 컬럼은 시계열의 모든 태그를 포함하고, 메트릭 이름이 있는 __name__ 태그도 포함해요. tags_to_columns 설정을 사용하면 특정 태그가 tags 컬럼 내부의 맵에 추가로 별도의 컬럼에도 저장되도록 지정할 수 있어요.

CREATE TABLE my_table
ENGINE = TimeSeries
SETTINGS tags_to_columns = {'instance': 'instance', 'job': 'job'}

이 문은 내부 tags target table에 instancejob 컬럼을 추가해요. 태그 instancejob의 값은 그 컬럼들과 tags 컬럼 둘 다에 저장돼요. ClickHouse의 구 버전이 만든 테이블에서 tags 컬럼은 전용 컬럼 없이 메트릭 이름도 없는 태그만 포함하고, all_tags 컬럼은 삽입 시 메트릭 이름을 제외한 모든 태그로 채워지는 임시(ephemeral) 컬럼이었어요.

내부 target table의 테이블 엔진

기본적으로 내부 target table은 다음 테이블 엔진을 사용해요.

  • samples 테이블은 MergeTree 사용
  • recent samples 테이블은 recent_samples_partition_by 설정에서 파생된 5시간 버킷으로 파티셔닝된 MergeTreerecent_samples_ttl_seconds 설정에서 파생된 TTL과 함께, 그리고 ttl_only_drop_parts를 활성화해서 사용. 그래서 만료된 파트는 통째로 버려짐
  • tags 테이블은 AggregatingMergeTree를 사용. 같은 데이터가 이 테이블에 여러 번 삽입되는 경우가 많아 중복 제거 방법이 필요하고, min_timemax_time 컬럼에 집계를 해야 하기 때문
  • metric families 테이블은 ReplacingMergeTree를 사용. 같은 데이터가 이 테이블에 여러 번 삽입되는 경우가 많아 중복 제거 방법이 필요하기 때문

생성된 내부 테이블의 엔진 패밀리는 default_table_engine 쿼리 레벨 설정을 따르며, default_table_engine = ReplicatedMergeTree 또는 SharedMergeTree이면 내부 테이블이 해당 Replicated 또는 Shared 엔진을 사용해요. default_table_engine = None(또는 다른 값)이면 내부 테이블의 엔진을 명시적으로 지정해야 해요.

모든 내부 테이블은 같은 복제 유형을 가져야 해요: 하나가 복제(또는 shared)되면 다른 내부 테이블도 복제(또는 shared)되어야 해요. 그렇지 않으면 복제본 간에 내용이 달라질 수 있어요. 예를 들어 SAMPLES INNER ENGINE = ReplicatedMergeTree(...)를 선언하려면 다른 내부 엔진도 복제되어야 해요 — 명시적으로 선언되거나 default_table_engine = ReplicatedMergeTree로 생성되거나.

지정하면 다른 테이블 엔진도 내부 target table에 사용할 수 있어요.

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES ENGINE=ReplicatedMergeTree
RECENT SAMPLES ENGINE=ReplicatedMergeTree
TAGS ENGINE=ReplicatedAggregatingMergeTree
METRIC FAMILIES ENGINE=ReplicatedReplacingMergeTree

tags 테이블은 태그 컬럼(과 tags Map)을 정렬 키 밖에 유지하는데, AggregatingMergeTree는 기본적으로 이를 거부해요 (allow_dimensions_outside_sorting_key 참고). 여기서는 안전한데, 그 컬럼들이 정렬 키의 일부인 id에 함수적으로 의존하므로 백그라운드 병합이 함께 축소하는 모든 행이 같은 값을 공유하기 때문이에요. 내부 tags 테이블이 생성되거나 엔진이 위처럼 인라인으로 지정되면 TimeSeriesallow_dimensions_outside_sorting_key = 1을 자동으로 설정해요. 수동으로 만든 external 집계 tags 테이블은 직접 설정해야 해요.

외부 target tables

TimeSeries 테이블이 수동으로 만든 테이블을 사용하게 할 수 있어요.

CREATE TABLE samples_for_my_table
(
    `id` UUID,
    `timestamp` DateTime64(3),
    `value` Float64
)
ENGINE = MergeTree
ORDER BY (id, timestamp);

CREATE TABLE tags_for_my_table ...

CREATE TABLE metric_families_for_my_table ...

CREATE TABLE my_table ENGINE=TimeSeries SAMPLES samples_for_my_table TAGS tags_for_my_table METRIC FAMILIES metric_families_for_my_table;

외부 테이블은 recent samples target으로도 사용할 수 있어요 (RECENT SAMPLES my_recent_samples_table 절). 그런 테이블은 외부 samples 테이블과 같은 컬럼을 가져야 하고, 최소 recent_samples_ttl_seconds초의 데이터를 유지해야 해요. 이는 사용자 책임이에요.

외부 테이블의 컬럼 타입(id, timestamp, value, 그리고 tags_to_columns에 나열된 <tag_value_column>들)은 TimeSeries 테이블이 내부에서 생성하는 것과 일치해야 해요 (Samples table, Tags table, Metric families table에서 타입 제약 참고). 타입 불일치는 CREATE 시점에 보고돼요.

외부 tags 테이블의 id 컬럼 타입과 식별자를 생성하는 표현식은 CREATE 시점에 id_typeid_generator 설정에 기록돼요 (버전 2부터). 그래서 TimeSeries 테이블의 정의가 그것들을 유지해요. 예를 들어 CREATE TABLE ... AS my_table은 외부 target table을 읽지 않고 my_table의 정의에서 id 타입을 읽어요. id_generator 설정이 지정되지 않으면 외부 테이블의 id 컬럼에 선언된 DEFAULT(있는 경우)로 설정되고, 그렇지 않으면 id 타입에서 파생된 표준 생성기로 설정돼요. 기록된 표현식은 외부 테이블의 DEFAULT가 나중에 바뀌어도 id를 생성하는 데 사용돼요 — 자세한 내용은 The id column을 참고해요.

설정 변경

CREATE 후에 두 설정을 변경할 수 있어요.

  • id_generator
  • filter_by_min_time_and_max_time
ALTER TABLE my_table MODIFY SETTING id_generator = 'sipHash64(tags)';
ALTER TABLE my_table MODIFY SETTING filter_by_min_time_and_max_time = 0;
ALTER TABLE my_table RESET SETTING filter_by_min_time_and_max_time;

tags 테이블에 이미 데이터가 있는 동안 id_generator를 변경하면 같은 metric+tag 조합에 대해 다른 ID가 생성될 수 있다는 점을 유의해요 — 이전 행은 이전 ID를 유지하고 새 행은 새 생성기를 사용해요.

다른 설정은 ALTER ... MODIFY SETTING으로 변경할 수 없어요: 대부분 CREATE 시점에 내부 테이블의 스키마에 구워지고, version 설정은 CREATE 시점에 자동으로 고정되어 스키마 자체를 식별해요 (Schema versioning 참고).

설정

TimeSeries 테이블을 정의할 때 지정할 수 있는 설정 목록이에요.

Name Type Default Description
id_type Data type id 컬럼에 따라 다름 target table의 id 컬럼 타입. 일반적으로 타입은 내부 테이블의 INNER COLUMNS 절이나 external tags 테이블에 선언됨; 타입이 정의에 달리 유지되지 않으면 설정이 CREATE 시점에 자동 기록됨: tags target이 외부 테이블이거나 id_generator 설정이 설정된 경우. TAGS INNER COLUMNS (id <type>) 대신 명시적으로 지정할 수도 있음. version이 최소 2여야 함
id_generator Expression id 타입에 따라 다름 태그에서 시계열의 식별자(지문)를 계산하는 표현식. 설정하지 않으면 id 컬럼의 기본 표현식이 사용됨. id 컬럼의 기본 표현식도 설정되지 않으면 표현식이 자동 선택됨. 외부 tags 테이블의 경우 version이 최소 2면 설정이 CREATE 시점에 자동 기록됨 (External target tables 참고)
tags_to_columns Map tags 테이블에서 어떤 태그를 별도의 컬럼에 넣을지 지정하는 맵. 문법: {'tag1': 'column1', 'tag2' : column2, ...}
use_all_tags_column_to_generate_id Bool false 사용되지 않는 설정, 아무것도 하지 않음
store_min_time_and_max_time Bool true true로 설정하면 테이블이 각 시계열의 min_timemax_time을 저장함
aggregate_min_time_and_max_time Bool true 내부 target tags 테이블을 만들 때 이 플래그는 min_time 컬럼의 타입으로 단순 Nullable(DateTime64(3)) 대신 SimpleAggregateFunction(min, Nullable(DateTime64(3)))을, 그리고 max_time 컬럼에 대해서도 같은 것을 사용 가능하게 함
filter_by_min_time_and_max_time Bool true true로 설정하면 테이블이 시계열 필터링에 min_timemax_time 컬럼을 사용함
samples_index_granularity UInt64 32768 내부 samples 테이블의 index_granularity 설정. 명시적으로 설정되면 엔진 선언의 index_granularity를 재정의함. 외부 samples 테이블과 비-MergeTree 엔진에서는 무시됨
recent_samples_ttl_seconds UInt64 345600 모든 삽입된 sample이 쓰이는 추가 recent samples target table의 보존 기간. 내부 recent samples 테이블은 항상 이 설정에서 파생된 TTL toDateTime(timestamp) + toIntervalSecond(recent_samples_ttl_seconds)를 얻음 (엔진 선언의 TTL 재정의); 외부 recent samples 테이블은 최소 이 초만큼 데이터를 유지해야 함. 시간 범위가 TTL 창에 맞는 쿼리는 기본 samples 테이블보다 recent samples 테이블을 선호함 (쿼리 레벨 설정 time_series_prefer_recent_samples_table 참고). 기본값은 4일; 유효 값은 CREATE 시점에 테이블 정의에 고정됨. 0으로 설정하면 recent samples 테이블이 비활성화됨
recent_samples_partition_by Expression toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) 내부 recent samples 테이블의 파티션 키. 예: toStartOfHour(timestamp). 명시적으로 설정되면 엔진 선언의 파티션 키를 재정의함; 둘 다 설정되지 않으면 5시간당 하나의 파티션. 외부 recent samples 테이블에서는 무시됨. recent_samples_ttl_seconds가 0이 아니어야 함
recent_samples_index_granularity UInt64 8192 내부 recent samples 테이블의 index_granularity 설정. 명시적으로 설정되면 엔진 선언의 index_granularity를 재정의함. 외부 recent samples 테이블과 비-MergeTree 엔진에서는 무시됨. recent_samples_ttl_seconds가 0이 아니어야 함
tags_index_granularity UInt64 8192 내부 tags 테이블의 index_granularity 설정. 명시적으로 설정되면 엔진 선언의 index_granularity를 재정의함. 외부 tags 테이블과 비-MergeTree 엔진에서는 무시됨
version UInt64 5 테이블의 버전: target table의 집합과 구조를 식별함. 버전은 테이블 생성 시 자동으로 고정되고 나중에 변경할 수 없으며, 일반적으로 CREATE TABLE 쿼리에서 생략해야 함 (Schema versioning 참고)

스키마 버전 관리(Schema versioning)

TimeSeries 테이블 엔진과 PromQL 실행 계층은 활발히 개발 중이에요: target table의 집합과 구조가 ClickHouse 버전 사이에서 바뀔 수 있어요. 그런 변경을 감지할 수 있게 모든 TimeSeries 테이블은 버전을 version 설정에 저장해요. 버전은 테이블이 생성될 때 CREATE 쿼리에 자동으로 고정되는데, 그 값은 서버가 아는 최신 버전(현재 5)이에요 — 테이블 메타데이터에 유지되고 ALTER로 변경할 수 없어요. 설정이 도입되기 전에 만들어진 테이블은 버전 0으로 간주돼요.

일반적으로 설정은 CREATE TABLE 쿼리에서 생략해야 해요 — 그러면 테이블이 최신 버전을 얻어요. 서버가 그 버전을 지원하면 명시적 version이 허용돼요. 그런 다음 테이블은 그 버전이 하는 방식으로 정의돼요 (Version history 참고). CREATE TABLE ... AS other_table은 다른 테이블의 버전을 복사하지 않아요. Creating a table AS existing table 참고.

서버는 버전 범위를 지원하며, SELECT로 읽기, INSERT 또는 Prometheus remote-write 프로토콜로 쓰기, PromQL 평가(prometheusQuery, prometheusQueryRange, timeSeriesSelector 테이블 함수, promql 방언, Prometheus HTTP 쿼리 API)에 대한 최소 버전이 다를 수 있어요.

  • TimeSeries 테이블의 버전이 PromQL에 너무 오래되었으면 그 테이블에 대한 PromQL 쿼리는 거부돼요. 예외는 테이블을 다시 만들 것을 제안해요: 새 TimeSeries 테이블을 만들고 INSERT ... SELECT 쿼리로 데이터를 복사한 다음 옛 테이블을 새 것으로 교체
  • 버전이 쓰기에 너무 오래되었으면 INSERT 쿼리와 Prometheus remote-write 프로토콜이 거부되는 반면 SELECT 쿼리는 여전히 동작해요
  • 버전이 서버에게 완전히 너무 오래되었으면 그 테이블에 대한 모든 쿼리(SHOW CREATE TABLE, DETACH, DROP 제외)가 거부돼요

버전 기록(Version history)

Version Changes
0 version 설정이 도입되기 전에 만들어진 테이블. target table의 컬럼을 외부 컬럼으로 선언한 "prealpha" 테이블과 recent samples 테이블이 없는 테이블 포함
1 version 설정이 도입됨
2 id_type 설정이 도입됨: 외부 tags 테이블이 있는 테이블은 id 컬럼 타입을 id_type에, 식별자를 생성하는 표현식을 id_generator에 기록하므로 그 정의가 외부 테이블에 의존하지 않음. id_generator가 설정되면 id_type도 기록됨 (The id column 참고)
3 외부 컬럼 time_seriessamples로 이름 변경됨 (Outer columns 참고). 이전 버전의 테이블은 컬럼의 옛 이름을 유지하고, prometheusQueryprometheusQueryRange 테이블 함수는 테이블이 사용하는 이름으로 컬럼을 반환함. 저장된 데이터는 변하지 않음
4 metrics target table이 metric families로 이름 변경됨: 내부 테이블은 .inner_id.metrics.<uuid> 대신 .inner_id.metricfamilies.<uuid>로 명명되고, 정의는 METRICS 대신 키워드 METRIC FAMILIES로 작성됨. 저장된 데이터는 변하지 않음
5 MergeTree 패밀리 엔진이 있는 새 내부 tags 테이블은 기본적으로 tags 맵에 keyValuePairs 텍스트 인덱스를 얻음 (Tags table 참고)

함수

인자로 TimeSeries 테이블을 지원하는 함수 목록:

더 알아보기 (Learn more)