개발/기술

Clickhouse Projections

kwony 2025. 11. 7. 20:39

Projections

 

Projections | ClickHouse Docs

Page describing what projections are, how they can be used to improve query performance, and how they differ from materialized views.

clickhouse.com

 

Projection 은 클릭하우스에서 속도를 올리기 위한 기술중 하나다. 

 

실질적으로 Projection 은 기존 테이블에 숨겨진 추가 테이블이라고 볼 수 있다. projection 은 기존 테이블과 행의 순서도 다르고 primary index 도 다르다. 데이터가 들어올 때마다 자동으로 그리고 점진적으로 aggregate values 들을 구해준다

 

Practically, a Projection can be thought of as an additional, hidden table to the original table. The projection can have a different row order, and therefore a different primary index, to that of the original table and it can automatically and incrementally pre-compute aggregate values

 

Projection 은 여러개의 행도 가지고 있고 삽입 시점에 미리 Aggregation 을 해준다는 점에서 Materialized Views 랑 비슷하다. 단 Materialized View 랑은 달리 기존 테이블 데이터를 주기적으로 싱크를 맞추고 있다는 점에서 다르다.

 

Original Table 에 대한 쿼리 요청이 들어오면 클릭하우스에서는 동일한 결과를 낼 수 있는 Projection 테이블을 선택하는데 로직은 아래와 같다. 

 

25.5 버전부터 projection 에 두가지 방식중 하나를 선택할 수 있는데 기존처럼 모든 칼럼을 저장할지 아니면 sortingkey +_part_offset

을 같이 이용하는 방법이 있다. 후자의 경우 기존 테이블에서 데이터를 읽어오는 방식이기 때문에 용량을 차지하지는 않지만 I/O 리소스가 발생한다.

 

예시;

 

PK 가 아닌 칼럼에 대해서 필터링을 걸고 싶을때

 

SELECT
  tip_amount,
  trip_id,
  dateDiff('minutes', pickup_datetime, dropoff_datetime) AS trip_duration_min
FROM nyc_taxi.trips WHERE tip_amount > 200 AND trip_duration_min > 0
ORDER BY tip_amount, trip_id ASC

 

위의 쿼리에서는 trip_duration_min 칼럼에 대해서 조건문을 걸어주고 있는데 이게 PK 가 아니기 때문에 성능이 좋지 않다

 

자주쓰이는 쿼리라면 여기에 projection 을 걸어줄 수 있다. 새로운 테이블을 만든건 아니지만 dateDiff 함수를 사용할대 아래 프로젝션 로직을 사용하게 된다

ALTER TABLE nyc_taxi.trips_with_projection
ADD PROJECTION prj_tip_amount
(
    SELECT *
    ORDER BY tip_amount, dateDiff('minutes', pickup_datetime, dropoff_datetime)
)

ALTER TABLE nyc.trips_with_projection MATERIALIZE PROJECTION prj_tip_amount

 

Projection 을 사용하고 나면 query_log 에 projection 을 사용했다고 나온다. 반복되는 쿼리 요청이 있다면 projection 사용해서 간편하게 처리해줄 수 있을듯하다

 

   ┌─query─────────────────────────────────────────────────────────────────────────┬─projections──────────────────────┐
   │ SELECT                                                                       ↴│ ['default.trips.prj_tip_amount'] │
   │↳  tip_amount,                                                                ↴│                                  │
   │↳  trip_id,                                                                   ↴│                                  │
   │↳  dateDiff('minutes', pickup_datetime, dropoff_datetime) AS trip_duration_min↴│                                  │
   │↳FROM trips WHERE tip_amount > 200 AND trip_duration_min > 0                   │                                  │
   └───────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────┘