1081 words
5 minutes
[clickHouse]Final keyword의 의미

01. Overview#

ClickHouse에서 FINAL 키워드는 MergeTree 계열 테이블에서 아직 물리적으로 Merge되지 않은 중복/삭제/버전 데이터를, 쿼리 시점에 강제로 정리해서 읽는 키워드

SELECT * FROM events FINAL WHERE user_id = 123;

-> background merge 결과를 기다리지 않고, 쿼리 시점에 논리적 최종 상태를 강제 계산

ClickHouse MergeTree 계열은 즉시 정합성이 아닌 eventual consistency + background merge 모델#

  • ClickHouse의 MergeTree 엔진은 데이터를 즉시 병합하지 않음
  • INSERT된 데이터는 별도의 Part로 저장되고, 나중 백그라운드에서 비동기적으로 Merge됨
  • 그래서 중간 상태가 존재함
[Part 1] user_id=1, status='active', version=1
[Part 2] user_id=1, status='inactive', version=2 ← 같은 키, 다른 Part
[Part 3] user_id=1, status='active', version=3

Merge 전 SELECT를 하면 3개 행이 모두 반환됨

02. ReplacingMergeTree 동작 원리#

ReplacingMergeTree는 동일한 ORDER BY 키를 가진 행 중 최신 버전만 유지하는 엔진이다.

CREATE TABLE user_status ㅓ(
user_id UInt64,
status String,
version UInt64
) ENGINE = ReplacingMergeTree(version)
ORDER BY user_id;
  • 문제는 유지의 시점
  • 기본적으로 중복 제거는 INSERT 시점이 아니라 Merge 시점에 일어남
  • Merge 타이밍은 비결정적이므로, 쿼리 시점에 중복이 보일 수 있음

03. FINAL 사용 시점#

적합한 상황#

  • 실시간 최신 상태 조회: 사용자 프로필, 주문 상태 등 “현재 값”이 필요할 때
  • 소규모 데이터 조회 : WHERE 조건으로 범위가 충분히 좁혀진 쿼리
  • 정확성 > 성능

Trade-off#

장점단점
쿼리 레벨에서 데이터 정합성 보장쿼리 성능 저하 (특히 대용량)
애플리케이션 로직 단순화병렬 처리 제한 가능성
Merge 완료 여부와 무관한 일관된 결과메모리 사용량 증가

04. FINAL 동작 방식과 성능 특성#

내부 동작#

FINAL이 붙으면 ClickHouse는:

  1. 관련된 모든 Part를 읽음
  2. ORDER BY 키 기준으로 행들을 정렬
  3. 중복 키에 대해 병합 로직 실행 (버전 비교 등)
  4. 최종 결과만 반환
┌─────────────────────────────────────────┐
│ FINAL 처리 흐름 │
├─────────────────────────────────────────┤
│ Part1 ──┐ │
│ Part2 ──┼──→ [Merge 연산] ──→ 결과 │
│ Part3 ──┘ (쿼리 시점) │
└─────────────────────────────────────────┘

성능 최적화#

-- 느림: 전체 테이블 스캔 + FINAL
SELECT * FROM events FINAL;
-- 빠름: Primary Key 필터링 + FINAL
SELECT * FROM events FINAL WHERE user_id = 123;

파티션 단위 최적화

SET do_not_merge_across_partitions_select_final = 1;

05. FINAL 대안들#

1. argMax를 활용한 수동 중복 제거#

SELECT
user_id,
argMax(status, version) AS latest_status
FROM user_status
GROUP BY user_id;

대용량 분석 쿼리에서 FINAL보다 효율적일 수 있다.

2. OPTIMIZE FINAL 강제 실행#

OPTIMIZE TABLE user_status FINAL;

백그라운드 Merge를 즉시 강제 실행한다. I/O 부하가 크므로 주의.

3. Materialized View로 사전 집계#

INSERT 시점에 집계를 수행해 조회 성능을 확보

방법장점단점
FINAL간단, 정확쿼리 성능 저하
argMax대용량에서 빠름쿼리 복잡도 증가
OPTIMIZE FINAL이후 조회 빠름I/O 부하

06. 사용 패턴#

Case 1: 단일 사용자 조회 - FINAL 적합

SELECT * FROM user_status FINAL WHERE user_id = 12345;

Case 2: 대시보드 집계 - argMax 권장

SELECT status, count() AS cnt
FROM (
SELECT user_id, argMax(status, version) AS status
FROM user_status GROUP BY user_id
)
GROUP BY status;

Concept

  • FINAL(clickHouse) : MergeTree 계열 테이블에서 아직 물리적으로 merge되지 않은 중복/삭제/버전을, 쿼리 시점에 강제로 정리해서 읽게하는 키워드
  • Part : INSERT된 데이터가 저장되는 불변의 데이터 조각, Merge 전까지 독립 존재
  • Merge(clickHouse) : 여러 Part를 하나로 병합하며 중복 제거/삭제 반영하는 백그라운드 작업
  • ReplacingMergeTree : 동일 ORDER BY 키의 행 중 최신 버전만 유지하는 clickhouse 엔진
  • argMax : 특정 컬럼이 최대값일 때의 다른 컬럼 값을 반환하는 집계 함수

Key Takeaways

  • FINAL은 물리적 Merge 없이도 쿼리 시점에 중복/삭제/버전을 정리해서 깨끗한 결과를 반환한다
  • 소규모 조회에는 FINAL이 적합하지만, 대용량 분석에서는 argMax 등 대안을 고려해야 한다
  • FINAL의 성능은 WHERE 조건으로 범위를 좁힐수록 개선된다.(index 태워야함)
  • 정확성 vs 성능 Trade-off에 따라 FINAL, argMax, OPTIMIZE FINAL 중 선택
[clickHouse]Final keyword의 의미
https://yjinheon.netlify.app/posts/02de/db/clickhouse/clickhouse_final/
Author
Datamind
Published at
2026-02-09
License
CC BY-NC-SA 4.0