TTL(Time-to-Live)은 ClickStack에서 효율적인 데이터 보존 및 관리를 위한 핵심 기능이며, 특히 방대한 양의 데이터가 지속적으로 생성되는 환경에서 더욱 중요합니다. TTL을 사용하면 오래된 데이터가 자동으로 만료되어 삭제되므로, 수동 개입 없이도 스토리지를 효율적으로 활용하고 성능을 유지할 수 있습니다. 이 기능은 데이터베이스를 경량으로 유지하고 스토리지 비용을 절감하며, 가장 관련성 높고 최신의 데이터에 집중함으로써 쿼리 성능을 빠르고 효율적으로 유지하는 데 필수적입니다. 또한 데이터 수명 주기를 체계적으로 관리해 데이터 보존 정책 관련 컴플라이언스를 지원하므로, 관측성 솔루션 전반의 지속 가능성과 확장성도 향상됩니다.
기본적으로 ClickStack은 데이터를 3일간 보존합니다. 이를 변경하려면 “TTL 수정”을 참조하십시오.
ClickHouse에서는 TTL을 테이블 수준에서 제어합니다. 예를 들어, logs의 스키마(schema)는 아래와 같습니다.
CREATE TABLE default.otel_logs
(
`Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
`TimestampTime` DateTime DEFAULT toDateTime(Timestamp),
`TraceId` String CODEC(ZSTD(1)),
`SpanId` String CODEC(ZSTD(1)),
`TraceFlags` UInt8,
`SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
`SeverityNumber` UInt8,
`ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
`Body` String CODEC(ZSTD(1)),
`ResourceSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
`ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`ScopeSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
`ScopeName` String CODEC(ZSTD(1)),
`ScopeVersion` LowCardinality(String) CODEC(ZSTD(1)),
`ScopeAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_scope_attr_key mapKeys(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_scope_attr_value mapValues(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_log_attr_value mapValues(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_body Body TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 8
)
ENGINE = MergeTree
PARTITION BY toDate(TimestampTime)
PRIMARY KEY (ServiceName, TimestampTime)
ORDER BY (ServiceName, TimestampTime, Timestamp)
TTL TimestampTime + toIntervalDay(3)
SETTINGS ttl_only_drop_parts = 1
ClickHouse의 파티셔닝은 컬럼 또는 SQL 표현식에 따라 디스크상에서 데이터를 논리적으로 분리할 수 있게 해줍니다. 데이터를 논리적으로 분리하면 각 파티션을 독립적으로 처리할 수 있으므로, 예를 들어 TTL 정책에 따라 만료된 데이터를 삭제할 수 있습니다.
위 예시에서 보듯이 파티셔닝은 테이블(table)을 처음 정의할 때 PARTITION BY 절로 지정합니다. 이 절에는 하나 이상의 컬럼에 대한 SQL 표현식이 들어갈 수 있으며, 그 결과에 따라 각 행이 어느 파티션으로 보내질지가 결정됩니다. 그 결과 데이터는 디스크의 각 파티션과 논리적으로 연결되며(공통 폴더 이름 접두사를 통해), 이후 특정 파티션만 분리해서 조회할 수 있습니다. 위 예시에서 기본 otel_logs 스키마(schema)는 toDate(Timestamp). 표현식을 사용해 일 단위로 파티셔닝합니다. 행이 ClickHouse에 삽입되면 이 표현식이 각 행에 대해 평가되며, 해당 결과의 파티션이 이미 있으면 그 파티션으로 라우팅됩니다(해당 날짜의 첫 번째 행이면 파티션이 생성됩니다). 파티셔닝과 그 밖의 활용 방식에 대한 자세한 내용은 “테이블 파티션”을 참조하십시오.
테이블 스키마에는 TTL TimestampTime + toIntervalDay(3)와 설정 ttl_only_drop_parts = 1도 포함됩니다. 앞의 절은 데이터가 3일이 지나면 삭제되도록 보장합니다. 설정 ttl_only_drop_parts = 1은 모든 데이터가 만료된 데이터 파트만 만료 대상으로 처리하도록 합니다(행을 부분적으로 삭제하려고 시도하는 대신). 또한 파티셔닝을 통해 서로 다른 날짜의 데이터가 절대 머지되지 않으므로 데이터를 효율적으로 삭제할 수 있습니다.
ttl_only_drop_parts항상 ttl_only_drop_parts=1 설정을 사용할 것을 권장합니다. 이 설정을 활성화하면 ClickHouse는 해당 파트의 모든 행이 만료되었을 때 파트 전체를 삭제합니다. ttl_only_drop_parts=0일 때 리소스를 많이 소비하는 뮤테이션으로 TTL이 적용된 행을 부분적으로 정리하는 대신, 파트 전체를 삭제하면 merge_with_ttl_timeout 시간을 더 짧게 설정할 수 있고 시스템 성능에 미치는 영향도 줄일 수 있습니다. TTL 만료를 수행하는 것과 동일한 단위(예: 일)로 데이터를 파티셔닝하면 파트에는 자연스럽게 정의된 인터벌의 데이터만 포함됩니다. 그러면 ttl_only_drop_parts=1을 효율적으로 적용할 수 있습니다.
기본적으로 TTL이 만료된 데이터는 ClickHouse가 데이터 파트를 머지할 때 제거됩니다. ClickHouse가 데이터가 만료되었음을 감지하면 예정되지 않은 머지를 수행합니다.
TTL 일정위에서 설명했듯이 TTL은 즉시 적용되지 않고 일정에 따라 적용됩니다. MergeTree 테이블 설정 merge_with_ttl_timeout은 delete TTL이 적용된 머지를 다시 수행하기 전까지의 최소 지연 시간을 초 단위로 설정합니다. 기본값은 14400초(4시간)입니다. 하지만 이는 최소 지연 시간일 뿐이며 TTL 머지가 실제로 트리거되기까지는 더 오래 걸릴 수 있습니다. 값이 너무 낮으면 예정되지 않은 머지가 많이 수행되어 많은 리소스를 소비할 수 있습니다. TTL 만료는 ALTER TABLE my_table MATERIALIZE TTL 명령으로 강제로 수행할 수 있습니다.
TTL은 다음 두 가지 방법으로 수정할 수 있습니다:
- 테이블 스키마를 수정합니다(권장). 이 방법을 사용하려면 clickhouse-client 또는 Cloud SQL Console을 사용해 ClickHouse 인스턴스에 연결해야 합니다. 예를 들어, 다음 DDL을 사용하면
otel_logs 테이블의 TTL을 수정할 수 있습니다:
ALTER TABLE default.otel_logs
MODIFY TTL TimestampTime + toIntervalDay(7);
- OTel collector를 수정합니다. ClickStack OpenTelemetry collector는 ClickHouse에 테이블이 없으면 생성합니다. 이는 ClickHouse exporter를 통해 수행되며, 이 exporter에는 기본 TTL 표현식을 제어하는 데 사용하는
ttl 매개변수가 있습니다. 예:
exporters:
clickhouse:
endpoint: tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1
ttl: 72h
위의 예시에서는 데이터를 테이블 수준에서 만료합니다. 데이터는 컬럼 수준에서도 만료할 수 있습니다. 데이터가 오래될수록 조사에 활용했을 때의 가치가 보관에 따른 리소스 오버헤드를 정당화하지 못하는 컬럼을 삭제하는 데 이를 사용할 수 있습니다. 예를 들어, 삽입 시점에 아직 추출되지 않은 새로운 동적 메타데이터(예: 새로운 Kubernetes 레이블)가 추가될 가능성에 대비해 Body 컬럼을 유지할 것을 권장합니다. 예를 들어 1개월 정도 지난 후에는 이러한 추가 메타데이터가 유용하지 않다는 점이 분명해질 수 있으므로 Body 컬럼을 계속 유지할 가치도 낮아집니다.
아래에서는 Body 컬럼을 30일 후 삭제하는 방법을 보여드립니다.
CREATE TABLE otel_logs_v2
(
`Body` String TTL Timestamp + INTERVAL 30 DAY,
`Timestamp` DateTime,
...
)
ENGINE = MergeTree
ORDER BY (ServiceName, Timestamp)
컬럼 수준 TTL을 지정하려면 스키마(schema)를 직접 정의해야 합니다. 이 설정은 OTel collector에서 지정할 수 없습니다.