지연 구체화
지연 구체화 (Lazy materialization)
이 문서는 지연 구체화(lazy materialization)가 어떻게 동작하고 ClickHouse의 더 넓은 I/O 최적화 스택에 어떻게 맞는지 설명합니다. 컬럼 읽기를 실제로 필요할 때까지 미뤄 쿼리 성능을 크게 개선하며, 25.4 버전부터 기본으로 켜져 있어요.
출처: 문서
본문
이 문서는 지연 구체화가 어떻게 동작하고 ClickHouse의 더 넓은 I/O 최적화 스택에 어떻게 맞는지 설명합니다. 지연 구체화가 쿼리 성능을 어떻게 높이는지 보여 주는 실세계 예제를 제시합니다.
25.4 버전부터 사용 가능 지연 구체화는 ClickHouse 25.4 버전에서 도입되었으며, 기본으로 켜져 있습니다.
개요 (Overview)
수년에 걸쳐 ClickHouse는 I/O를 공격적으로 줄이기 위한 일련의 계층적 최적화를 도입했습니다. 이러한 기법들이 속도와 효율성의 기반을 형성합니다:
| 최적화 | 설명 |
|---|---|
| 컬럼형 스토리지 (Columnar storage) | 쿼리에 필요하지 않은 전체 컬럼을 건너뛸 수 있게 하고, 비슷한 값을 함께 그룹핑해 높은 압축을 가능하게 하여 데이터 로딩 중 I/O를 최소화합니다. |
| 희소 기본 인덱스 | 보조 데이터 스킵 인덱스 |
| PREWHERE | 인덱스되지 않은 컬럼의 필터에 대해서도 일치를 확인해, 그렇지 않으면 로드했다가 버릴 데이터를 일찍 건너뜁니다. 독립적으로 동작하거나 인덱스가 선택한 granule을 다듬을 수 있으며, 모든 컬럼 필터와 일치하지 않는 행을 건너뛰는 것으로 granule 가지치기를 보완합니다. |
| 쿼리 조건 캐시 (Query condition cache) | 마지막으로 모든 필터와 일치한 granule을 기억해 반복 쿼리를 빠르게 합니다. 그러면 ClickHouse는 쿼리 형태가 바뀌어도 일치하지 않은 granule을 읽고 필터링하는 것을 건너뛸 수 있습니다. |
앞서 언급한 I/O 최적화는 읽는 데이터를 크게 줄일 수 있지만, 여전히 WHERE 절을 통과한 행에 대한 모든 컬럼이 정렬·집계·LIMIT 같은 연산을 실행하기 전에 로드되어야 한다고 가정합니다. 그런데 일부 컬럼이 나중에까지 필요하지 않다면, 또는 WHERE 절을 통과했음에도 어떤 데이터가 전혀 필요하지 않다면 어떨까요?
여기가 지연 구체화가 개입하는 곳입니다. 그것은 I/O 최적화 스택을 완성하는 직교적인 향상입니다:
- 인덱싱은
PREWHERE와 함께WHERE절의 컬럼 필터와 일치하는 행만 처리되게 합니다. - 지연 구체화는 이것을 기반으로 쿼리 실행 계획이 실제로 요구할 때까지 컬럼 읽기를 미룹니다.
필터링 후에도 다음 연산 — 예: 정렬 — 에 필요한 컬럼만 즉시 로드됩니다. 나머지는 연기되고,
LIMIT덕분에 최종 결과를 만들기에 충분한 만큼만 종종 부분적으로 읽힙니다. 이것은 지연 구체화를 Top N 쿼리에 특히 강력하게 만듭니다. 최종 결과가 종종 큰 컬럼들의 몇 행만 필요할 수 있기 때문입니다.
작업 예제
블로그 포스트 "ClickHouse gets lazier (and faster): Introducing lazy materialization"를 지연 구체화의 심층 분석으로 강력히 권장합니다. 아래 예제는 위 블로그 포스트에서 가져온 것으로, 지연 구체화로 ClickHouse 쿼리가 219초에서 단 139밀리초(1576배 속도 향상)로 가는 방법을 시연하기 위해 여기 재현했습니다.
인덱싱과 PREWHERE의 이점을 얻으려면 쿼리에 필터가 필요합니다. 기본 키 컬럼용으로는 인덱싱, 모든 컬럼용으로는 PREWHERE가 필요합니다. 지연 구체화는 그 위에 깔끔하게 겹쳐지지만, 앞서 언급한 다른 최적화와 달리 아예 컬럼 필터가 없는 쿼리도 가속화할 수 있습니다.
도움이 되는 투표 수가 가장 많은 Amazon 리뷰를 날짜·제품·평점·인증 상태와 무관하게 찾아, 제목·헤드라인·전문과 함께 상위 3개를 반환하는 다음 예제 쿼리를 고려해 보세요.
먼저 (콜드 파일시스템 캐시로) 지연 구체화를 비활성화한 채 쿼리를 실행합니다(query_plan_optimize_lazy_materialization 사용):
쿼리
SELECT
helpful_votes,
product_title,
review_headline,
review_body
FROM amazon.amazon_reviews
ORDER BY helpful_votes DESC
LIMIT 3
FORMAT Vertical
SETTINGS
query_plan_optimize_lazy_materialization = false;
응답
Row 1:
──────
helpful_votes: 47524
product_title: Kindle: Amazon's Original Wireless Reading Device (1st generation)
review_headline: Why and how the Kindle changes everything
review_body: This is less a \"pros and cons\" review than a hopefully use...
Row 2:
──────
helpful_votes: 41393
product_title: BIC Cristal For Her Ball Pen, 1.0mm, Black, 16ct (MSLP16-Blk)
review_headline: FINALLY!
review_body: Someone has answered my gentle prayers and FINALLY designed ...
Row 3:
──────
helpful_votes: 41278
product_title: The Mountain Kids 100% Cotton Three Wolf Moon T-Shirt
review_headline: Dual Function Design
review_body: This item has wolves on it which makes it intrinsically swee...
0 rows in set. Elapsed: 219.071 sec. Processed 150.96 million rows, 71.38 GB (689.08 thousand rows/s., 325.81 MB/s.)
Peak memory usage: 1.11 GiB.
다음으로 (다시 콜드 파일시스템 캐시로) 쿼리를 다시 실행하되 이번에는 지연 구체화를 활성화합니다:
쿼리
SELECT
helpful_votes,
product_title,
review_headline,
review_body
FROM amazon.amazon_reviews
ORDER BY helpful_votes DESC
LIMIT 3
FORMAT Vertical
SETTINGS
query_plan_optimize_lazy_materialization = true;
보통은 지연 구체화의 이점을 얻기 위해 query_plan_optimize_lazy_materialization = true를 명시적으로 설정할 필요가 없습니다. 기본으로 활성화되어 있습니다.
응답
Row 1:
──────
helpful_votes: 47524
product_title: Kindle: Amazon's Original Wireless Reading Device (1st generation)
review_headline: Why and how the Kindle changes everything
review_body: This is less a \"pros and cons\" review than a hopefully use...
Row 2:
──────
helpful_votes: 41393
product_title: BIC Cristal For Her Ball Pen, 1.0mm, Black, 16ct (MSLP16-Blk)
review_headline: FINALLY!
review_body: Someone has answered my gentle prayers and FINALLY designed ...
Row 3:
──────
helpful_votes: 41278
product_title: The Mountain Kids 100% Cotton Three Wolf Moon T-Shirt
review_headline: Dual Function Design
review_body: This item has wolves on it which makes it intrinsically swee...
0 rows in set. Elapsed: 0.139 sec. Processed 150.96 million rows, 1.81 GB (1.09 billion rows/s., 13.06 GB/s.)
Peak memory usage: 3.80 MiB.
지연 구체화를 끈 경우와 켠 경우의 성능 차이를 고려해 보세요:
| 메트릭 | 지연 구체화 꺼짐 | 지연 구체화 켜짐 | 개선 |
|---|---|---|---|
| 경과 시간 | 219.071 sec | 0.139 sec | ~1576× 빠름 |
| 읽은 데이터 | 71.38 GB | 1.81 GB | ~40× 적음 |
| 최고 메모리 | 1.11 GiB | 3.80 MiB | ~300× 적음 |
쿼리 실행 계획에서 지연 구체화 확인
EXPLAIN 절로 쿼리의 논리적 실행 계획을 검사해 이전 쿼리에 대한 지연 구체화의 사용을 관찰할 수 있습니다:
EXPLAIN actions = 1
SELECT
helpful_votes,
product_title,
review_headline,
review_body
FROM amazon.amazon_reviews
ORDER BY helpful_votes DESC
LIMIT 3
SETTINGS
query_plan_optimize_lazy_materialization = true;
...
Lazily read columns: review_headline, review_body, product_title
Limit
Sorting
ReadFromMergeTree
연산자 계획을 아래에서 위로 읽으면, ClickHouse가 세 개의 큰 String 컬럼 읽기를 정렬과 제한 이후로 미룸을 관찰할 수 있습니다.