TTL로 데이터 관리
TTL로 데이터 관리 (Manage data with TTL)
TTL(time-to-live)은 일정 시간이 지난 후 행이나 컬럼을 이동·삭제·롤업하게 하는 기능입니다. 오래된 데이터 삭제 외에도 디스크 간 데이터 이동(핫/웜/콜드 아키텍처)과 데이터 롤업에도 쓰여요.
출처: 문서
본문
TTL 개요
TTL(time-to-live)은 일정 시간이 지난 후 행이나 컬럼이 이동, 삭제, 롤업되게 하는 기능을 말합니다. "time-to-live"라는 표현이 오래된 데이터를 삭제하는 데만 적용되는 것처럼 들리지만, TTL에는 여러 사용 사례가 있습니다:
- 오래된 데이터 제거: 당연히, 지정된 시간 간격 후에 행이나 컬럼을 삭제할 수 있습니다.
- 디스크 간 데이터 이동: 일정 시간이 지나면 스토리지 볼륨 간에 데이터를 이동할 수 있습니다 — 핫/웜/콜드 아키텍처를 배포하는 데 유용합니다.
- 데이터 롤업: 삭제하기 전에 오래된 데이터를 다양한 유용한 집계와 계산으로 롤업합니다.
TTL은 전체 테이블이나 특정 컬럼에 적용할 수 있습니다.
TTL 문법
TTL 절은 컬럼 정의 뒤와/또는 테이블 정의의 끝에 나타날 수 있습니다. INTERVAL 절로 시간 길이(Date 또는 DateTime 데이터 타입이어야 함)를 정의하세요. 예를 들어 다음 테이블은 TTL 절이 있는 두 컬럼을 가집니다:
CREATE TABLE example1 (
timestamp DateTime,
x UInt32 TTL timestamp + INTERVAL 1 MONTH,
y String TTL timestamp + INTERVAL 1 DAY,
z String
)
ENGINE = MergeTree
ORDER BY tuple()
- x 컬럼은 timestamp 컬럼으로부터 1개월의 수명(time to live)을 가집니다.
- y 컬럼은 timestamp 컬럼으로부터 1일의 수명을 가집니다.
- 간격이 지나면 컬럼이 만료됩니다. ClickHouse는 컬럼 값을 해당 데이터 타입의 기본값으로 대체합니다. 데이터 파트의 모든 컬럼 값이 만료되면 ClickHouse는 파일시스템의 데이터 파트에서 이 컬럼을 삭제합니다.
TTL 규칙은 변경하거나 삭제할 수 있습니다. 자세한 내용은 "Manipulations with Table TTL" 페이지를 참고하세요.
모범 사례. 테이블 수준 TTL로 오래된 행을 제거할 때는 TTL 표현식에 사용된 동일한 시간 필드의 날짜 또는 월로 테이블을 파티셔닝할 것을 권장합니다. ClickHouse는 개별 행을 삭제하는 것보다 전체 파티션을 훨씬 효율적으로 삭제할 수 있습니다. 파티션 키가 TTL 표현식과 일치하면 ClickHouse는 행을 제거하기 위해 데이터 파트를 다시 쓰는 대신, 파티션이 만료될 때 한 번에 전체 파티션을 삭제할 수 있습니다. 파티션 입자(granularity)는 TTL 기간에 맞춰 선택하세요:
- TTL이 수일/수주면
toYYYYMMDD(date_field)로 일 단위 파티셔닝. - TTL이 수개월/수년이면
toYYYYMM(date_field)또는toStartOfMonth(date_field)로 월 단위 파티셔닝.
TTL 이벤트 트리거
만료된 행의 삭제나 집계는 즉시가 아니며, 테이블 병합 중에만 발생합니다. (어떤 이유로든) 활발히 병합되지 않는 테이블이 있다면, TTL 이벤트를 트리거하는 두 설정이 있습니다:
merge_with_ttl_timeout: 삭제 TTL로 병합을 반복하기 전의 최소 지연(초). 기본값은 14400초(4시간)입니다.merge_with_recompression_ttl_timeout: 재압축 TTL(삭제 전 데이터를 롤업하는 규칙)로 병합을 반복하기 전의 최소 지연(초). 기본값: 14400초(4시간).
따라서 기본적으로 TTL 규칙은 최소 4시간마다 테이블에 적용됩니다. TTL 규칙을 더 자주 적용해야 한다면 위 설정을 수정하면 됩니다.
좋은 해결책은 아니지만(자주 사용하길 권장하지도 않지만), OPTIMIZE를 사용해 병합을 강제할 수도 있습니다:
OPTIMIZE TABLE example1 FINAL
OPTIMIZE는 테이블 파트의 비예약 병합을 초기화하고, 테이블이 이미 단일 파트라면 FINAL이 재최적화를 강제합니다.
행 제거
일정 시간 후 테이블에서 전체 행을 제거하려면 테이블 수준에서 TTL 규칙을 정의하세요:
CREATE TABLE customers (
timestamp DateTime,
name String,
balance Int32,
address String
)
ENGINE = MergeTree
ORDER BY timestamp
TTL timestamp + INTERVAL 12 HOUR
또한 레코드 값에 기반한 TTL 규칙을 정의하는 것도 가능합니다. where 조건을 지정해 쉽게 구현할 수 있습니다. 여러 조건이 허용됩니다:
CREATE TABLE events
(
`event` String,
`time` DateTime,
`value` UInt64
)
ENGINE = MergeTree
ORDER BY (event, time)
TTL time + INTERVAL 1 MONTH DELETE WHERE event != 'error',
time + INTERVAL 6 MONTH DELETE WHERE event = 'error'
컬럼 제거
전체 행을 삭제하는 대신 balance와 address 컬럼만 만료되길 원한다고 가정해 봅시다. customers 테이블을 수정해 두 컬럼 모두 2시간 TTL을 추가하겠습니다:
ALTER TABLE customers
MODIFY COLUMN balance Int32 TTL timestamp + INTERVAL 2 HOUR,
MODIFY COLUMN address String TTL timestamp + INTERVAL 2 HOUR
롤업 구현
일정 시간이 지난 후 행을 삭제하되 일부 데이터는 보고용으로 유지하고 싶다고 가정해 봅시다. 모든 세부 사항은 원하지 않고, 과거 데이터의 몇 가지 집계 결과만 필요합니다. 이는 TTL 표현식에 GROUP BY 절을 추가하고, 집계 결과를 저장할 테이블의 일부 컬럼을 추가해 구현할 수 있습니다.
다음 hits 테이블에서 오래된 행을 삭제하되, 행을 제거하기 전에 hits 컬럼의 합과 최대값은 유지하고 싶다고 가정해 봅시다. 그 값들을 저장할 필드가 필요하고, 합과 최대값을 롤업하는 TTL 절에 GROUP BY 절을 추가해야 합니다:
CREATE TABLE hits (
timestamp DateTime,
id String,
hits Int32,
max_hits Int32 DEFAULT hits,
sum_hits Int64 DEFAULT hits
)
ENGINE = MergeTree
PRIMARY KEY (id, toStartOfDay(timestamp), timestamp)
TTL timestamp + INTERVAL 1 DAY
GROUP BY id, toStartOfDay(timestamp)
SET
max_hits = max(max_hits),
sum_hits = sum(sum_hits);
hits 테이블에 대한 몇 가지 참고:
TTL절의GROUP BY컬럼은PRIMARY KEY의 접두사여야 하며, 하루의 시작으로 그룹핑하고 싶습니다. 따라서toStartOfDay(timestamp)가 기본 키에 추가되었습니다.- 집계 결과를 저장할 두 필드
max_hits와sum_hits를 추가했습니다. SET절이 정의된 방식에 기반해, 로직이 동작하려면max_hits와sum_hits의 기본값을hits로 설정하는 것이 필요합니다.
핫/웜/콜드 아키텍처 구현
ClickHouse Cloud를 사용한다면 이 단원의 단계는 적용되지 않습니다. ClickHouse Cloud에서는 오래된 데이터를 옮기는 것에 대해 걱정할 필요가 없습니다.
대량의 데이터를 다룰 때 흔한 관행은 데이터가 오래됨에 따라 그 데이터를 이동하는 것입니다. ClickHouse에서 TTL 명령의 TO DISK와 TO VOLUME 절을 사용해 핫/웜/콜드 아키텍처를 구현하는 단계는 다음과 같습니다. (참고로 핫과 콜드일 필요는 없습니다 — 어떤 사용 사례든 TTL로 데이터를 이동할 수 있습니다.)
TO DISK와TO VOLUME옵션은 ClickHouse 구성 파일에 정의된 디스크나 볼륨의 이름을 가리킵니다. 디스크를 정의하는my_system.xml(또는 어떤 파일 이름이든)이라는 새 파일을 만들고, 디스크를 사용하는 볼륨을 정의하세요. 구성이 시스템에 적용되도록 XML 파일을/etc/clickhouse-server/config.d/에 놓으세요:
<clickhouse>
<storage_configuration>
<disks>
<default>
</default>
<hot_disk>
<path>./hot/</path>
</hot_disk>
<warm_disk>
<path>./warm/</path>
</warm_disk>
<cold_disk>
<path>./cold/</path>
</cold_disk>
</disks>
<policies>
<default>
<volumes>
<default>
<disk>default</disk>
</default>
<hot_volume>
<disk>hot_disk</disk>
</hot_volume>
<warm_volume>
<disk>warm_disk</disk>
</warm_volume>
<cold_volume>
<disk>cold_disk</disk>
</cold_volume>
</volumes>
</default>
</policies>
</storage_configuration>
</clickhouse>
- 위 구성은 ClickHouse가 읽고 쓸 수 있는 폴더를 가리키는 세 디스크를 나타냅니다. 볼륨은 하나 이상의 디스크를 담을 수 있습니다 — 세 디스크 각각에 대해 볼륨을 정의했습니다. 디스크를 확인해 봅시다:
SELECT name, path, free_space, total_space
FROM system.disks
┌─name────────┬─path───────────┬───free_space─┬──total_space─┐
│ cold_disk │ ./data/cold/ │ 179143311360 │ 494384795648 │
│ default │ ./ │ 179143311360 │ 494384795648 │
│ hot_disk │ ./data/hot/ │ 179143311360 │ 494384795648 │
│ warm_disk │ ./data/warm/ │ 179143311360 │ 494384795648 │
└─────────────┴────────────────┴──────────────┴──────────────┘
- 그리고...볼륨을 확인해 봅시다:
SELECT
volume_name,
disks
FROM system.storage_policies
┌─volume_name─┬─disks─────────┐
│ default │ ['default'] │
│ hot_volume │ ['hot_disk'] │
│ warm_volume │ ['warm_disk'] │
│ cold_volume │ ['cold_disk'] │
└─────────────┴───────────────┘
- 이제 데이터를 핫, 웜, 콜드 볼륨 사이로 이동시키는
TTL규칙을 추가합니다:
ALTER TABLE my_table
MODIFY TTL
trade_date TO VOLUME 'hot_volume',
trade_date + INTERVAL 2 YEAR TO VOLUME 'warm_volume',
trade_date + INTERVAL 4 YEAR TO VOLUME 'cold_volume';
- 새
TTL규칙은 구체화되어야 하지만, 강제로 확실히 할 수도 있습니다:
ALTER TABLE my_table
MATERIALIZE TTL
system.parts테이블로 데이터가 예상한 디스크로 이동했는지 확인하세요:
Using the system.parts table, view which disks the parts are on for the crypto_prices table:
SELECT
name,
disk_name
FROM system.parts
WHERE (table = 'my_table') AND (active = 1)
응답은 다음과 같습니다:
┌─name────────┬─disk_name─┐
│ all_1_3_1_5 │ warm_disk │
│ all_2_2_0 │ hot_disk │
└─────────────┴───────────┘