[Clickhouse] 쿼리 최적화: 테이블 Projection
이름이 좀 애매해서 헷갈릴 수 있는데, Clickhouse에서 Projection라고 부르는 것은 사실 인덱스에 가깝다.
Clickhouse는 Order By에 걸린 정렬키에 기반했을때만 최적의 조회/필터 성능을 보장할 수 있다.
정렬키가 아닌 일반 컬럼으로도 괜찮은 성능을 보여주지만 베스트는 아닌 것이다.
그래서 기본키 외에 다른 컬럼으로도 빠른 필터링이나 정렬 등을 하고 싶다면, 해당 컬럼에 대한 Projection 조합을 만드는 것이 주요한 최적화 접근법 중 하나다.
이건 내부적으로 사실상 또 하나의 테이블을 만드는 식으로 동작한다.
name에 대한 Projection을 만든다면, 해당 name 필드를 정렬키로 하는 hidden 테이블을 만드는 것이다.
https://clickhouse.com/docs/guides/best-practices/sparse-primary-indexes#option-3-projections
그러면 쿼리를 실행시킬때 조건에 따라서 projection으로 구성된 테이블을 기반으로 스캔을 할 수도, 아닐 수도 있다.
그리고 이건 진짜 테이블을 별도로 만드는거라서, 그만큼의 디스크 I/O 사용량과 디스크 저장공간이 늘어난다.
막 만들게 아니라 잘 생각하고 만들어야 한다.
생성 방법
프로젝션은 다음과 같은 방법으로 생성할 수 있다.
ALTER TABLE 테이블명 ADD PROJECTION 프로젝션명 (
SELECT
*
ORDER BY 정렬키
)
저 정렬키에 검색에 필요한 키를 나열하면 된다.
그러면 그 기준에 맞춰서 projection 필드를 그대로 만들어준다.
근데 저러면 프로젝션 테이블의 크기가 커질 수 있다.
가능하다면 집어넣을 컬럼도 필요에 따라 조정하면 좋다.
ALTER TABLE 테이블명 ADD PROJECTION 프로젝션명 (
SELECT
id, name
ORDER BY 정렬키
)
혹은, 애초부터 통계를 상정하고 프로젝션을 만들 수도 있다.
ALTER TABLE 테이블명 ADD PROJECTION 프로젝션명 (
SELECT
user_agent,
sum(pages_visited)
GROUP BY user_id, user_agent
)
꽤 유연한 편이다.
사용해보기
한번 슬로쿼리를 최적화해보겠다.
이건 기본 정렬키가 아닌 create_dt라는 필드로 부담스런 범위 필터를 걸어놨기 때문에, 성능이 좀 구리게 나오는 쿼리다.
어쩔 수 없다. clickhouse는 기본적으로 정렬키 기반 접근에만 최적의 필터 성능을 보장하기 때문이다.
그러면 지금 필터를 걸고 있는 컬럼에 대해 프로젝션을 추가해보자.

그러면 빨라지기도 좀 빨라지고

스캔 관점에서도 기본 정렬키로 Scan하던 것이

추가한 프로젝션 기반으로 스캔을 하게끔 동작 방식이 바뀐다.

참조
https://clickhouse.com/docs/sql-reference/statements/alter/projection
https://clickhouse.com/docs/guides/best-practices/sparse-primary-indexes#option-3-projections