홈으로 돌아가기

ClickHouse의 TTL: 데이터 수명 주기 관리

이 문서는 ClickHouse의 내장 TTL 메커니즘을 설명합니다: 오래된 행 삭제, HDD 또는 S3로 이동(계층형 스토리지), GROUP BY를 통한 세부 데이터 요약 집계, GDPR을 위한 개인 열 익명화. 백그라운드 프로세스 구성, MATERIALIZE TTL 및 파티셔닝과의 조합을 다룹니다.

ClickHouse TTL: 완벽한 데이터 관리 가이드
Advertisement 728x90

ClickHouse의 TTL: 자동 데이터 수명 주기 관리

1. TTL이 필요한 이유 – '삭제를 잊어버린' 문제

온라인 카지노로 돌아가 보겠습니다. 모든 플레이어의 베팅을 저장한다고 가정해 봅시다. 한 달 후 테이블은 500GB, 1년 후에는 5TB가 됩니다. 디스크가 가득 차고 쿼리가 느려지며 오래된 데이터는 점점 덜 필요해집니다. 비즈니스 소유자는 "플레이어는 최근 30일의 베팅만 확인하고, 보고서에는 오래된 기간의 집계된 합계만 필요하다"고 말합니다.

파티셔닝 글에서 배운 대로 하루에 한 번 오래된 파티션을 삭제하는 스크립트를 작성할 수 있습니다. 하지만 외부 스케줄러(cron), 별도 스크립트, 실행 모니터링, 오류 처리가 필요합니다.

**TTL(Time-To-Live)**은 데이터베이스 수준에서 이 문제를 해결합니다. ClickHouse의 내장 메커니즘으로 자동으로:

Google AdInline article slot
  • 오래된 행을 삭제하고,
  • 더 저렴한 디스크(HDD, S3)로 이동하며,
  • 오래된 데이터를 집계(세부 정보를 요약으로 축소)하고,
  • 개인 데이터를 익명화합니다(GDPR).

이 모든 작업은 백그라운드에서 사용자 개입 없이 SQL로 정의한 일정에 따라 수행됩니다.

실생활 비유: TTL은 창고를 임대하는 것과 같습니다. 소유주와 합의합니다: "30일 이상 보관된 상품은 먼 구석 저렴한 구역으로 옮기고, 1년 이상 보관된 것은 폐기하세요." 소유주가 기한을 추적하고 작업을 수행하므로 매번 알릴 필요가 없습니다.

2. 행 수준 TTL – 오래된 레코드 삭제

가장 간단한 옵션: 행이 특정 시간 동안 유지된 후 삭제됩니다.

Google AdInline article slot

CREATE TABLE with TTL

-- 행이 90일 동안 유지되는 bets 테이블 생성
CREATE TABLE bets
(
    user_id     UInt64,
    amount      Decimal(18,2),
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 90 DAY;   -- created_at 이후 90일이 지나면 행이 삭제됨

여기서 일어나는 일:

  • TTL created_at + INTERVAL 90 DAY – 각 행에 대해 만료 날짜가 계산됩니다: created_at + 90일. 현재 날짜(today())가 이 날짜를 초과하면 행이 삭제 대상으로 표시됩니다.
  • 백그라운드 프로세스(보통 하루에 한 번)가 granule을 검사하고 TTL이 만료된 행을 삭제합니다.
  • 삭제는 파트 수준에서 발생합니다. ClickHouse는 삭제된 행 없이 파트를 다시 씁니다.

ALTER TABLE – TTL 추가 또는 변경

핵심 기능: 테이블을 다시 만들지 않고 기존 테이블에 TTL을 추가할 수 있습니다.

-- 기존 테이블에 TTL 추가
ALTER TABLE bets MODIFY TTL created_at + INTERVAL 90 DAY;

-- 수명을 90일에서 180일로 변경
ALTER TABLE bets MODIFY TTL created_at + INTERVAL 180 DAY;

-- TTL 제거 (데이터가 영원히 저장됨)
ALTER TABLE bets REMOVE TTL;

왜 중요한가? 실제로 저장 요구 사항은 변경됩니다. 처음에는 모든 것을 영원히 저장해야 한다고 생각했지만, 백업이 공간을 차지하고 분석가가 오래된 데이터를 필요로 하지 않는다는 것을 알게 됩니다. MODIFY TTL을 사용하면 한 줄로 규칙을 변경할 수 있습니다.

Google AdInline article slot

100억 개의 행이 있는 테이블에 TTL을 추가하면 어떻게 될까요? 끔찍한 일은 없습니다. ClickHouse는 데이터를 즉시 다시 쓰지 않습니다. 단순히 백그라운드에서 점진적으로 TTL을 적용하기 시작합니다. 다음 파트 병합 중에 오래된 행이 제외됩니다.

3. 디스크 이동이 있는 TTL (계층형 스토리지)

때로는 데이터를 삭제하기 아깝지만 빠르고 비싼 SSD에 저장하는 것은 비용이 많이 듭니다. 해결책: 오래된 데이터를 느리고 저렴한 HDD(또는 클라우드 스토리지 S3)로 이동합니다.

먼저 ClickHouse 설정(config.xml)에서 디스크를 구성합니다:

<storage_configuration>
    <disks>
        <ssd>
            <path>/mnt/ssd/clickhouse/</path>
        </ssd>
        <hdd>
            <path>/mnt/hdd/clickhouse/</path>
        </hdd>
    </disks>
    <policies>
        <hot_to_cold>
            <volumes>
                <hot>
                    <disk>ssd</disk>
                </hot>
                <cold>
                    <disk>hdd</disk>
                </cold>
            </volumes>
        </hot_to_cold>
    </policies>
</storage_configuration>

이제 TTL 이동이 있는 테이블을 생성합니다:

CREATE TABLE bets_tiered
(
    user_id     UInt64,
    amount      Decimal(18,2),
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 30 DAY TO DISK 'hdd';   -- 30일 후 HDD로 이동

어떤 일이 발생하나요: 행은 처음 30일 동안 빠른 SSD에 저장됩니다(보고서에서 가장 자주 필요할 때). 30일 후 ClickHouse는 백그라운드에서 데이터 파트를 HDD로 이동합니다. 오래된 기간의 데이터를 쿼리할 때는 계속 사용할 수 있지만 약간 느려집니다.

이동 후 삭제를 결합할 수 있습니다:

-- 30일 SSD, 그 다음 90일까지 HDD, 그 후 삭제
CREATE TABLE bets_multi_ttl
(
    user_id UInt64,
    amount Decimal(18,2),
    created_at DateTime
)
ENGINE = MergeTree()
ORDER BY user_id
TTL
    created_at + INTERVAL 30 DAY TO DISK 'hdd',
    created_at + INTERVAL 90 DAY DELETE;

비유: 호텔: 처음 30일은 스위트룸(빠르고 비쌈)에 살고, 그 다음에는 일반실(느리고 저렴)로 이동하며, 90일 후에는 퇴실합니다. 모두 자동입니다.

4. S3로 이동하는 TTL

ClickHouse는 클라우드 스토리지(Amazon S3, MinIO, Google Cloud Storage)와 함께 작동할 수 있습니다. S3를 사용하여 storage_policy를 구성하고 오래된 데이터를 클라우드로 이동할 수 있으며, 스토리지 비용은 매우 저렴합니다.

설정(간략화):

<storage_configuration>
    <disks>
        <s3>
            <type>s3</type>
            <endpoint>https://s3.amazonaws.com/mybucket/clickhouse/</endpoint>
            <access_key_id>AKIAIOSFODNN7EXAMPLE</access_key_id>
            <secret_access_key>wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</secret_access_key>
        </s3>
    </disks>
    <policies>
        <s3_policy>
            <volumes>
                <hot>
                    <disk>default</disk>   <!-- 로컬 SSD -->
                </hot>
                <cold_s3>
                    <disk>s3</disk>        <!-- 클라우드 -->
                </cold_s3>
            </volumes>
        </s3_policy>
    </policies>
</storage_configuration>

S3로 TTL이 있는 테이블:

CREATE TABLE bets_s3
(
    user_id UInt64,
    amount Decimal(18,2),
    created_at DateTime
)
ENGINE = MergeTree()
ORDER BY created_at
TTL created_at + INTERVAL 90 DAY TO VOLUME 'cold_s3';   -- 90일 후 S3로

왜 멋진가요: S3의 기가바이트당 월 비용은 매우 저렴합니다. 데이터는 분석에 계속 사용할 수 있지만(로컬 디스크보다 느림), 서버 공간 부족을 걱정할 필요가 없습니다. S3는 무한합니다.

5. 집계를 위한 TTL (가장 강력한 기능)

이것은 제가 가장 좋아하는 패턴입니다. 오래된 세부 데이터를 삭제하는 대신 집계로 축소합니다. 예를 들어, 7일보다 오래된 베팅은 초 단위로 필요하지 않지만 일별 및 사용자별 합계는 필요합니다.

-- 세부 베팅 테이블
CREATE TABLE bets_detailed
(
    user_id     UInt64,
    bet_id      String,
    amount      Decimal(18,2),
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 7 DAY
    GROUP BY toDate(created_at) AS day, user_id
    SET total_bets = sum(amount),           -- 해당 일의 모든 베팅 합계
        bet_count = count()                 -- 해당 일의 베팅 수
    DELETE WHERE day < now() - INTERVAL 90 DAY;   -- 90일 후 집계도 삭제

분석:

  • TTL created_at + INTERVAL 7 DAY – 행(세부 베팅)이 생성된 후 7일이 지나면 세부 정보가 사라집니다.
  • GROUP BY toDate(created_at) AS day, user_id – 행이 일과 사용자별로 그룹화됩니다. 한 사용자의 하루에 대한 수천 개의 세부 베팅 대신 하나의 집계 행이 생성됩니다.
  • SET total_bets = sum(amount), bet_count = count() – 새 집계 행에서 필드는 집계 함수로 채워집니다.
  • DELETE WHERE day < now() - INTERVAL 90 DAY – 90일보다 오래된 집계는 최종적으로 삭제됩니다.

실제로 일어나는 일:

  1. 0–7일: 데이터가 세부 형태로 저장됩니다. 모든 베팅을 분석할 수 있습니다.
  2. 7–90일: 세부 베팅이 (user_id, day)당 하나의 행으로 축소되며 total_betsbet_count 필드가 있습니다. 공간 사용량이 10~100배 감소합니다.
  3. 90일 이후: 집계도 삭제되고 백업만 남습니다.

비유: 시간별 항목이 있는 일기를 씁니다. 일주일 후 시간별 항목을 일별 요약(합계)으로 다시 작성합니다. 그리고 3개월 후에는 일별 요약도 버리고 월별 보고서만 유지합니다.

6. 특정 열에 대한 TTL (GDPR 익명화)

법률(GDPR, 개인 데이터)에 따라 개인 정보(이메일, IP 주소)를 특정 기간 이상 저장하지 않아야 합니다. 그러나 익명 분석(베팅 금액, 게임 수)은 영원히 저장할 수 있습니다.

TTL은 전체 행이 아닌 개별 열에 적용할 수 있습니다.

-- 개인 데이터가 있는 테이블
CREATE TABLE user_events
(
    user_id     UInt64,
    email       String,           -- 개인 열
    ip_address  String,           -- 개인 열
    event_type  String,
    event_value UInt64,
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL
    email + INTERVAL 1 YEAR,      -- 1년 후 이메일이 null로 설정됨
    ip_address + INTERVAL 1 YEAR, -- 1년 후 IP가 null로 설정됨
    created_at + INTERVAL 10 YEAR; -- 10년 후 전체 행이 삭제됨

어떤 일이 발생하나요: 행이 생성된 후 1년이 지나면 emailip_address 열이 기본값(문자열의 경우 빈 문자열, 숫자의 경우 0)으로 대체됩니다. 데이터는 분석에 유용하게 남아 있습니다(어떤 사용자가 있었지만 정확히 누군지는 알 수 없음). 10년 후 전체 행이 삭제됩니다.

GDPR에 중요한 이유: 수동 스크립트 없이 자동으로 법률을 준수합니다. 감사관이 와서 TTL 규칙을 보고 개인 데이터가 허용된 기간보다 더 오래 저장되지 않았는지 확인할 수 있습니다.

7. 백그라운드 TTL 프로세스 구성

TTL은 만료 후 즉시 트리거되지 않습니다. ClickHouse는 다음을 수행하는 백그라운드 프로세스를 실행합니다:

  • granule 검사,
  • TTL 규칙 적용,
  • 오래된 데이터 없이 또는 수정된 열로 파트 다시 쓰기.

구성 매개변수(config.xml 또는 SET 사용):

-- TTL 프로세스 실행 간격 (기본값 1일)
ALTER SYSTEM MODIFY SETTING merge_with_ttl_timeout = 86400;  -- 초 단위

-- 테스트를 위해 더 자주 실행할 수 있음
SET merge_with_ttl_timeout = 3600;   -- 매 시간

TTL을 너무 자주 실행하면 안 되는 이유: TTL 적용은 CPU와 디스크에 부하를 주는 데이터 파트 다시 쓰기 작업입니다. 하루에 한 번이면 충분합니다. 테라바이트 규모의 데이터가 있는 경우 매 시간 실행하면 방해가 될 수 있습니다.

TTL이 작동하는지 확인하는 방법:

-- 활성 TTL이 있는 파트 확인
SELECT 
    partition,
    name,
    rows,
    modification_time,
    has_ttl_info   -- 1 = 이 파트에 TTL이 적용됨
FROM system.parts
WHERE table = 'bets' AND active = 1;

8. MATERIALIZE TTL – 강제 적용

때로는 백그라운드 프로세스를 기다리지 않고 지금 당장 TTL을 적용해야 할 때가 있습니다. 예:

  • 방대한 테이블에 TTL을 추가했고 즉시 오래된 데이터를 정리하려는 경우.
  • TTL 규칙을 테스트 중이고 하루를 기다리고 싶지 않은 경우.
-- 전체 테이블에 TTL 강제 적용
ALTER TABLE bets MATERIALIZE TTL;

-- 특정 파티션에만 적용 (더 빠름)
ALTER TABLE bets MATERIALIZE TTL IN PARTITION '202501';

어떤 일이 발생하나요: ClickHouse는 테이블(또는 파티션)의 모든 파트를 스캔하고 모든 TTL 규칙을 즉시 적용합니다. 대규모 테이블에서는 몇 분에서 몇 시간이 걸릴 수 있습니다. 피크 시간에는 수행하지 마십시오.

사용 시기: 야간, 유지보수 중, 또는 이미 죽은 데이터를 백업하지 않기 위해 백업 전.

9. 실제 사용 사례: GDPR 및 집계가 있는 카지노

이제 모든 것을 종합해 보겠습니다. 온라인 카지노에서 이벤트를 저장하기 위한 완전한 스키마를 상상해 보십시오.

-- 원시 이벤트 (각 베팅, 각 게임)
CREATE TABLE raw_events
(
    user_id         UInt64,
    session_id      String,
    ip_address      String,           -- GDPR 민감
    event_type      String,           -- 'bet', 'win', 'login'
    event_value     Int64,
    created_at      DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL
    -- 세부 이벤트는 30일 동안 저장
    created_at + INTERVAL 30 DAY DELETE,
    -- IP 주소는 14일 후 제거 (GDPR)
    ip_address + INTERVAL 14 DAY,
    -- 오래된 데이터 집계: 30일 후 일별 요약으로 축소
    created_at + INTERVAL 30 DAY
        GROUP BY toDate(created_at) AS day, user_id
        SET total_bets = sumIf(event_value, event_type = 'bet'),
            total_wins = sumIf(event_value, event_type = 'win'),
            sessions_count = countDistinct(session_id)
        DELETE WHERE day < now() - INTERVAL 2 YEAR;   -- 집계는 2년 동안 저장

달성한 것:

  • 0–14일: IP 주소를 포함한 전체 정보. 사고 조사, 다중 계정 탐지 가능.
  • 15–30일: IP 주소는 이미 null 처리(익명화)되었지만 세부 이벤트는 여전히 존재. 지리적 위치 없이 사용자 행동 분석 가능.
  • 31일 – 2년: 세부 이벤트가 삭제됨. 대신 (day, user)당 집계 행이 있음. 공간 사용량이 100배 감소. 대시보드가 빠르게 작동.
  • 2년 이상: 모든 것이 삭제됨. S3의 백업만 남음(백업을 만든 경우).

TTL 후 집계 데이터를 읽는 방법:

-- 이제 테이블은 혼합됨: 세부 행(처음 30일)과 집계(최대 2년)
-- 일반 집계 쿼리를 작성하면 두 유형의 행 모두에서 작동
SELECT 
    toDate(created_at) AS day,
    user_id,
    sum(event_value) AS total
FROM raw_events
WHERE created_at >= today() - 45
GROUP BY day, user_id;

ClickHouse는 신선한 데이터의 경우 세부 행을 합산하고 오래된 데이터의 경우 미리 계산된 집계를 사용합니다. 마법입니다.

10. TTL을 사용하지 말아야 할 경우와 대안

TTL이 적합하지 않은 경우:

  • 데이터가 자주 업데이트됩니다. TTL은 마지막 업데이트가 아닌 삽입 시 트리거됩니다. CollapsingMergeTree로 취소와 함께 행을 수정하면 TTL은 원래 created_at부터 계산됩니다. 해결책: TTL 표현식에 updated_at 열을 사용하십시오.

  • 정확한 시간 제어가 필요합니다. TTL 프로세스는 백그라운드이며 정확하지 않습니다. 자정에 정확히 삭제를 보장해야 하는 경우 작동하지 않습니다. 지연은 최대 몇 시간까지 가능합니다.

  • 병합이 드문 매우 큰 테이블. TTL은 파트 병합 중에 적용됩니다. 테이블이 드물게 병합되는 경우(예: merge_with_ttl_timeout 설정으로 인해) 오래된 데이터가 더 오래 남아 있을 수 있습니다.

TTL의 대안:

접근 방식 사용 시기 장점 단점
파티셔닝 + DROP PARTITION 데이터가 지속적으로 유입되고 달력별로 삭제 즉시 삭제, 오버헤드 없음 외부 스케줄러 필요, 유연성 부족(파티션만 가능)
TTL DELETE 데이터가 지연되어 도착할 수 있고 행 수명별 삭제 내장, 유연함, 외부 스크립트 불필요 부정확한 삭제 시간, 병합 부하
TTL TO DISK 데이터를 유지해야 하지만 저렴한 스토리지에 스토리지 비용 절감, 쿼리에 투명 storage_policy 구성 필요
TTL GROUP BY 세부 데이터가 필요 없고 집계만 필요 급격한 공간 감소(100배 이상) 세부 정보 손실, 디버깅 복잡성

최상의 결과를 위해 파티셔닝과 결합

모범 사례: 파티셔닝 + TTL을 함께 사용하십시오.

CREATE TABLE bets_optimized
(
    user_id UInt64,
    amount Decimal(18,2),
    created_at DateTime
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)           -- 월별 파티션
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 30 DAY DELETE;    -- 30일 TTL

이것이 좋은 이유:

  • 파티션은 전체 월을 빠르게 삭제하는 데 도움이 됩니다(TTL이 지연되는 경우).
  • TTL은 파티션 내에서 더 유연하게 정리합니다(달력이 아닌 행 수명 기준).

다음 단계

이제 ClickHouse에서 자동 데이터 관리를 위한 모든 도구를 갖추었습니다. 다음 주제:

  • 고급 스토리지 정책 – 각 단계에서 다른 TTL로 SSD → HDD → S3로 자동 이동을 설정하는 방법.
  • TTL 모니터링 – system.query_log를 사용하여 얼마나 많은 데이터가 삭제되고 있는지와 속도를 추적하는 방법.
  • TTL + 구체화된 뷰 – 세부 데이터와 혼합하지 않고 오래된 데이터를 별도의 테이블로 자동 집계하는 방법.

요약: ClickHouse의 TTL은 데이터 수명 주기 관리를 위한 스위스 군용 칼입니다. 삭제, 이동, 집계, 익명화가 가능합니다. 정리에는 TTL ... DELETE, 비용 절감에는 TTL ... TO DISK, 집계 마법에는 TTL ... GROUP BY, GDPR에는 열에 TTL을 사용하십시오. 그리고 규칙을 즉시 적용해야 할 때는 MATERIALIZE TTL을 잊지 마십시오.


이전 글:
다음 글: ClickHouse의 딕셔너리: JOIN 없이 빠른 조회

— Editorial Team

Advertisement 728x90

다음 읽기