LLM WikiAccess-protected knowledge portal
← 스터디 홈
2편 · 약 20분

ClickHouse 26.6: 10주년 기념 릴리스의 운영자 관점 체크리스트

10년의 릴리스 중 가장 큰 숫자

2026년 7월, ClickHouse가 10주년을 맞아 26.6을 공개했다. 56개 새 기능, 79개 성능 최적화, 366개 버그 수정이 담겼다. 버전 번호가 연도.월 방식이라 26.6은 2026년 6월 릴리스를 뜻한다. 숫자 자체보다 중요한 것은 이번 릴리스가 운영자가 오랫동안 원하던 기능 세 가지를 함께 처음 내놓았다는 점이다.

  • 인덱스를 실제로 만들기 전에 효과를 미리 측정하는 가상 skip 인덱스
  • 독립 스케줄로 뒤처지던 다단계 Materialized View를 종속 갱신으로 연결
  • 중첩 쿼리 분석 비용을 줄여 동일 쿼리를 약 3× 빠르게 실행

세 기능은 공통 배경을 공유한다. ClickHouse를 실제 운영하면서 만나는 반복적인 병목이다. 인덱스가 효과가 있는지 사전에 알 방법이 없었고, RMV(Refreshable Materialized View) 체인이 한 단계 늦으면 다음 단계가 낡은 데이터를 읽었으며, 복잡한 쿼리를 던지면 분석 시간이 길어졌다.

이 장은 각 기능의 동작 원리, 운영 경계, 그리고 도입 시 확인해야 할 사항을 다룬다.

이 장은 ClickHouse 26.6 공식 릴리스 블로그, changelog, 관련 기술 자료를 기준으로 한다. 실험적(Experimental) 표시가 붙은 기능은 별도 표기한다.


가상 skip 인덱스: 만들기 전에 먼저 측정한다

ClickHouse의 skip 인덱스(data skipping index)는 특정 컬럼 표현식에 대한 집계를 granule 단위로 저장해, 쿼리가 조건과 일치할 수 없는 granule을 통째로 건너뛰게 한다. bloom filter, minmax, set, ngrambf_v1 등 여러 타입이 있다.

문제는 효과가 있는지 알려면 인덱스를 실제로 만들어봐야 했다는 것이다. 수백 GB 테이블에 bloom filter를 추가하면 materialization에 시간과 공간이 필요하다. 인덱스를 만든 뒤 EXPLAIN indexes=1로 확인했더니 granule 건너뜀이 없다면, 시간과 공간을 낭비하고 되돌려야 한다.

EXPLAIN WHATIF와 가상 인덱스

26.6은 다음 흐름을 가능하게 한다.

-- 1. 세션 범위 가상 인덱스 정의 (실제 테이블에 기록되지 않음)
CREATE HYPOTHETICAL INDEX idx_bloom
  ON events (action) TYPE bloom_filter(0.01);

-- 2. 해당 인덱스가 있다고 가정하고 쿼리 분석
EXPLAIN WHATIF
  SELECT count() FROM events WHERE action = 'purchase';

-- 3. 가상 인덱스 목록 확인
SELECT * FROM system.hypothetical_indexes;

EXPLAIN WHATIF는 실제로 쿼리를 실행하지 않는다. 가상 인덱스가 존재한다고 가정한 실행 계획을 보여주고, 예상 skip ratio와 비용 절감을 함께 제공한다. 테이블에 아무 변경도 없다.

가상 인덱스는 세션 범위다. 연결이 끊어지면 사라진다. 여러 타입과 표현식을 반복해 측정하고, 결과가 납득될 때만 ALTER TABLE ... ADD INDEX로 실체화한다.

가상 인덱스 평가 → 실체화 결정 흐름 CREATE HYPOTHETICAL INDEX 세션 범위, 테이블 무변경 EXPLAIN WHATIF 실행 계획 + skip ratio 실제 실행 없음 skip ratio 확인 granule 건너뜀이 유의미한가? ALTER TABLE ADD INDEX 실체화. 비용 납득됨. 충분함 인덱스 타입 변경 또는 인덱스 포기 부족함 운영 맥락 기존 방식 (26.5 이하) → 인덱스 실체화 (시간+공간) → 쿼리 실행 후 확인 → 효과 없으면 DROP + 재시도 비용: 디스크+시간, 반복 가능 26.6 WHATIF 방식 → CREATE HYPOTHETICAL INDEX → EXPLAIN WHATIF 즉시 결과 → skip ratio 확인 후 결정 비용: 세션 메모리만 주의할 점 skip ratio가 높아도 실제 쿼리 시간이 줄지 않는 경우 있음 (데이터 분포, granule 크기, I/O 병목 확인 필요)
ClickHouse 26.6 가상 skip 인덱스 평가 흐름

skip 인덱스 타입 선택 기준

타입적합한 컬럼주의점
bloom_filter(p)고 카디널리티 문자열, UUIDFPR p가 작을수록 인덱스 크기 증가
minmax날짜, 숫자, 정렬 경향이 있는 값범위가 넓은 granule에 효과 없음
set(max_values)낮은 카디널리티 enum·상태값max_values 초과 시 항상 포함으로 처리
ngrambf_v1LIKE 검색 텍스트짧은 문자열은 효과 제한적
tokenbf_v1단어 단위 텍스트 검색언어 패턴에 따라 달라짐

26.6 이전에는 이 선택을 실험 없이 해야 했다. 이제는 여러 타입을 CREATE HYPOTHETICAL INDEX로 반복해 EXPLAIN WHATIF로 skip ratio를 비교한 뒤 결정할 수 있다.


종속 Refreshable Materialized View: 체인이 시계처럼 돌아가게

RMV(Refreshable Materialized View)는 ClickHouse의 매우 유용한 기능이다. REFRESH EVERY 1 HOUR처럼 주기를 정하면 ClickHouse가 알아서 쿼리를 재실행하고 결과를 갱신한다.

문제는 다단계 집계 체인에서 드러난다.

raw_events (실시간 데이터)
  → stage1_rmv (REFRESH EVERY 1 HOUR: 시간별 집계)
    → stage2_rmv (REFRESH EVERY 1 HOUR: 일별 요약)
      → stage3_rmv (REFRESH EVERY 1 HOUR: 주별 리포트)

세 RMV가 각자 독립적인 시간 기반 스케줄을 갖는다. stage1_rmv가 00:05에 완료됐는데 stage2_rmv가 00:02에 이미 실행되면, stage2는 이전 주기의 낡은 데이터를 읽는다. 스케줄이 조금만 어긋나도 1시간 데이터 지연이 생길 수 있다.

REFRESH DEPENDS ON

26.6은 시간 기반 스케줄을 다른 RMV의 갱신 이벤트로 대체한다.

-- stage1: 원본 데이터를 시간별로 집계
CREATE MATERIALIZED VIEW stage1_rmv
  REFRESH EVERY 1 HOUR
  AS SELECT toStartOfHour(ts) AS hour, sum(value) AS total
     FROM raw_events GROUP BY hour;

-- stage2: stage1이 갱신된 직후에만 실행
CREATE MATERIALIZED VIEW stage2_rmv
  REFRESH EVERY 1 HOUR DEPENDS ON stage1_rmv
  AS SELECT toDate(hour) AS day, sum(total) AS day_total
     FROM stage1_rmv GROUP BY day;

-- stage3: stage2가 갱신된 직후에만 실행
CREATE MATERIALIZED VIEW stage3_rmv
  REFRESH EVERY 1 HOUR DEPENDS ON stage2_rmv
  AS SELECT toStartOfWeek(day) AS week, sum(day_total) AS week_total
     FROM stage2_rmv GROUP BY week;

DEPENDS ON은 두 가지를 보장한다.

  1. stage1_rmv가 완료된 뒤에만 stage2_rmv가 실행된다.
  2. stage2_rmv가 완료된 뒤에만 stage3_rmv가 실행된다.

시간 트리거를 유지하면서 실행 순서를 강제하는 구조다. REFRESH EVERY 1 HOUR는 여전히 실행 주기의 하한을 정하지만, 실제 실행은 dependency가 완료된 뒤다.

이전과의 차이

항목26.5 이하26.6 DEPENDS ON
실행 트리거각자 독립 시간 타이머upstream RMV 갱신 완료
데이터 신선도upstream 지연 + 타이머 오차upstream 완료 직후 실행
스케줄 드리프트장기 운영 시 오차 누적 가능구조적으로 방지
실패 전파upstream 실패와 무관하게 진행upstream 실패 시 downstream도 대기

마지막 항목은 주의가 필요하다. stage1_rmv가 실패하면 stage2_rmv는 실행을 기다린다. 자동 재시도 설정이 없으면 체인 전체가 멈출 수 있다. 장기 대기 상태를 모니터링하고 알림을 연결해야 한다.


중첩 쿼리 3× 지연 개선

26.6에서 측정된 수치다.

동일 쿼리:
  - ClickHouse 26.5: 최적 98ms
  - ClickHouse 26.6: 최적 30ms
  약 3.3× 개선

대상은 깊이 중첩된 서브쿼리다. ClickHouse 26.5까지는 복잡한 중첩 구조를 분석할 때 쿼리 분석기(analyzer) 내부 비용이 실행 비용과 비슷하게 커지는 경우가 있었다. 26.6은 이 분석 경로를 더 효율적으로 만들었다.

어떤 쿼리가 해당하는가?

-- 전형적인 중첩 집계 패턴
SELECT user_id, total_value
FROM (
  SELECT user_id, sum(day_value) AS total_value
  FROM (
    SELECT user_id, toDate(ts) AS day, sum(amount) AS day_value
    FROM (
      SELECT user_id, ts, amount
      FROM events
      WHERE status = 'completed'
    ) GROUP BY user_id, day
  ) GROUP BY user_id
) WHERE total_value > 1000;

CTE, 다단계 집계, 여러 단계의 필터링이 결합된 분석 쿼리에서 이 개선이 유효하다. 단순한 SELECT나 한 단계 JOIN은 이 최적화의 주요 대상이 아니다.

실제 워크로드에 얼마나 적용되는지는 EXPLAIN으로 중첩 깊이를 확인하고 26.5와 26.6에서 실행 시간을 비교해 측정해야 한다. 내 쿼리가 26.6 최적화 경로에 해당하는지는 직접 측정 없이 알 수 없다.


system.documentation: 서버에서 직접 문서를 쿼리한다

ClickHouse 26.6은 system.documentation 테이블을 추가했다. 이 테이블은 ClickHouse의 레퍼런스 매뉴얼을 서버 내부에 저장하고, SQL로 검색할 수 있게 한다.

-- 특정 함수 문서 검색
SELECT name, description, example
FROM system.documentation
WHERE name LIKE '%bloom%'
ORDER BY name;

-- built-in CLI help도 같은 내용을 참조
\h bloom_filter

실용적인 용도는 두 가지다. 첫째, ClickHouse 버전별로 지원 함수와 설정 키가 다를 때 클라이언트 연결만으로 현재 서버 기준 문서를 확인할 수 있다. 둘째, WHERE name = 'current_feature'로 찾은 결과를 다른 쿼리와 JOIN하거나, BI 도구에서 함수 목록을 자동으로 가져오는 데 활용할 수 있다.


실험적 기능: Continuous Query

26.6에는 실험적(Experimental) 표시가 붙은 continuous query가 포함됐다. 일반 쿼리는 한 번 실행되고 끝나지만, continuous query는 지정된 Kafka·MaterializeMySQL 같은 스트리밍 소스를 읽으면서 지속 실행된다.

-- 실험적 기능 활성화 필요
SET allow_experimental_continuous_query = 1;

CREATE CONTINUOUS QUERY cq_sensor_agg
  TO sensor_hourly
AS SELECT sensor_id, toStartOfHour(ts) AS hour, avg(value) AS avg_val
   FROM sensor_stream
   GROUP BY sensor_id, hour;

이 기능은 Kafka 소비와 집계를 하나의 선언으로 표현한다. 이전까지는 MaterializeMySQL이나 Kafka 테이블 엔진 + Materialized View 조합으로 비슷한 효과를 얻었는데, 해당 구조의 명시적 대안이 생긴다.

주의: 실험적 기능이므로 API와 동작이 마이너 릴리스에서 바뀔 수 있다. 프로덕션 사용 전 해당 patch release의 release note와 known issue를 반드시 확인한다.


26.6 업그레이드 체크리스트

업그레이드 전 준비
프로덕션 스냅샷 또는 백업을 확인한다. 26.6은 대형 릴리스이며 변경 범위가 넓다.
현재 사용 중인 skip 인덱스 목록과 설정을 기록한다. 인덱스 동작이 미묘하게 달라질 수 있다.
기존 RMV 체인의 스케줄과 의존성을 다이어그램으로 정리한다. DEPENDS ON 마이그레이션 후보를 식별한다.
366개 버그 수정 중 자신의 워크로드와 관련된 항목을 changelog에서 확인한다.
가상 skip 인덱스 도입
튜닝 대상 쿼리를 먼저 선정한다. 실행 시간이 길고 full granule scan이 의심되는 쿼리부터 시작한다.
CREATE HYPOTHETICAL INDEX로 후보 인덱스를 세션에 정의하고 EXPLAIN WHATIF로 skip ratio를 측정한다.
skip ratio가 높아도 I/O 병목이 아닌 경우 실제 쿼리 시간이 개선되지 않을 수 있다. 실체화 전 반드시 테스트 환경에서 실행 시간을 비교한다.
RMV 체인 DEPENDS ON 마이그레이션
기존 RMV의 스케줄 드리프트 발생 여부를 system.view_refreshes에서 확인한다.
신규 체인은 REFRESH EVERY ... DEPENDS ON으로 선언하고 테스트 환경에서 실행 순서를 확인한다.
upstream 실패 시 downstream 대기 상태 알림을 연결한다. system.view_refresheslast_refresh_result를 모니터링한다.
기존 운영 중인 RMV를 변경할 때는 DEPENDS ON 추가가 DDL이므로 롤백 절차를 준비한다.
중첩 쿼리 최적화 검증
대표 중첩 쿼리를 26.5와 26.6에서 동일 조건으로 실행하고 실행 시간을 비교한다. 3× 개선은 특정 패턴에 해당하며 모든 쿼리에 적용되지 않는다.
실행 계획 변경 여부를 EXPLAIN으로 확인한다. 옵티마이저 변화로 기존과 다른 조인 순서나 집계 경로가 선택될 수 있다.
ClickHouse 26.6 업그레이드 운영 gate

모니터링 쿼리 모음

업그레이드 후 확인해야 할 핵심 지표를 쿼리로 표현한다.

-- RMV 갱신 상태 확인 (DEPENDS ON 체인 포함)
SELECT view_name, last_refresh_time, last_refresh_result,
       next_refresh_time, exception
FROM system.view_refreshes
ORDER BY last_refresh_time DESC;

-- 가상 인덱스 목록 (세션 내)
SELECT * FROM system.hypothetical_indexes;

-- skip 인덱스 granule 건너뜀 통계
SELECT table, index_name, skipped_granules, total_granules,
       round(skipped_granules / total_granules * 100, 1) AS skip_pct
FROM system.parts_columns
WHERE index_name != '';

-- 중첩 쿼리 실행 시간 분포 (query_log 기반)
SELECT
  query_duration_ms,
  read_rows,
  memory_usage
FROM system.query_log
WHERE query LIKE '%FROM (%FROM (%'
  AND event_date = today()
ORDER BY query_duration_ms DESC
LIMIT 20;

정리

ClickHouse 26.6은 세 가지 문제를 동시에 해결한다.

가상 skip 인덱스는 "인덱스가 효과가 있는지 만들어보기 전에 알 수 없다"는 한계를 없앤다. skip ratio를 세션 내에서 측정하고 납득된 뒤에만 실체화한다. 잘못된 인덱스를 만들고 되돌리는 반복 비용이 사라진다.

DEPENDS ON RMV 체인은 독립 타이머로 인한 스케줄 드리프트를 구조적으로 막는다. upstream이 완료된 직후 downstream이 실행되므로 다단계 집계의 신선도가 유지된다. 단, upstream 실패 알림을 빠뜨리면 체인 전체가 조용히 멈출 수 있다.

중첩 쿼리 최적화는 특정 패턴에서 약 3× 지연 개선을 가져온다. 내 쿼리가 해당하는지는 26.5와 비교 측정이 필요하다. 일반화해서 "모든 쿼리가 빨라진다"고 말하지 않는다.

운영자가 26.6을 적용할 때 가장 먼저 할 일은 테스트 환경에서 대표 쿼리와 RMV 체인을 재현하는 것이다. 366개 버그 수정이 기대한 쪽뿐 아니라 예상하지 못한 동작을 바꿀 수도 있다.

References