쿼리 성능

쿼리 성능 (Query performance)

시계열 워크로드의 쿼리 성능을 ORDER BY 키 최적화와 머티어리얼라이즈드 뷰로 개선하는 방법을 살펴봐요.

출처: 문서

본문

스토리지를 최적화한 다음 단계는 쿼리 성능 개선이에요. 이 섹션은 두 가지 핵심 기법을 탐구해요: ORDER BY 키 최적화와 머티어리얼라이즈드 뷰 사용. 이러한 접근 방식이 쿼리 시간을 초 단위에서 밀리초 단위로 줄이는 방법을 볼 수 있어요.

ORDER BY 키 최적화

다른 최적화를 시도하기 전에 ClickHouse가 가능한 가장 빠른 결과를 만들도록 정렬 키를 최적화해야 해요. 올바른 키 선택은 실행할 쿼리에 크게 의존해요. 대부분의 쿼리가 projectsubproject 컬럼으로 필터링한다고 가정해요. 이 경우 정렬 키에 추가하는 것이 좋아요 — 시간으로도 조회하므로 time 컬럼도 마찬가지로요. wikistat과 같은 컬럼 타입을 가지지만 (project, subproject, time)으로 정렬된 테이블 버전을 하나 더 만들어 볼게요.

CREATE TABLE wikistat_project_subproject
(
    `time` DateTime,
    `project` String,
    `subproject` String,
    `path` String,
    `hits` UInt64
)
ENGINE = MergeTree
ORDER BY (project, subproject, time);

정렬 키 표현식이 성능에 얼마나 중요한지 알아보기 위해 여러 쿼리를 비교해 볼게요. 이전의 데이터 타입과 코덱 최적화를 적용하지 않았으므로, 쿼리 성능 차이는 정렬 순서에만 기반한 거예요.

쿼리 (time) (project, subproject, time)
SELECT project, sum(hits) AS h
FROM wikistat
GROUP BY project
ORDER BY h DESC
LIMIT 10;
2.381 sec 1.660 sec
SELECT subproject, sum(hits) AS h
FROM wikistat
WHERE project = 'it'
GROUP BY subproject
ORDER BY h DESC
LIMIT 10;
2.148 sec 0.058 sec
SELECT toStartOfMonth(time) AS m, sum(hits) AS h
FROM wikistat
WHERE (project = 'it') AND (subproject = 'zero')
GROUP BY m
ORDER BY m DESC
LIMIT 10;
2.192 sec 0.012 sec
SELECT path, sum(hits) AS h
FROM wikistat
WHERE (project = 'it') AND (subproject = 'zero')
GROUP BY path
ORDER BY h DESC
LIMIT 10;
2.968 sec 0.010 sec

머티어리얼라이즈드 뷰

또 다른 옵션은 머티어리얼라이즈드 뷰를 사용해 인기 있는 쿼리의 결과를 집계하고 저장하는 거예요. 이 결과는 원본 테이블 대신 조회할 수 있어요. 다음 쿼리가 우리 경우에 꽤 자주 실행된다고 가정해요:

SELECT path, SUM(hits) AS v
FROM wikistat
WHERE toStartOfMonth(time) = '2015-05-01'
GROUP BY path
ORDER BY v DESC
LIMIT 10
┌─path──────────────────┬────────v─┐
│ -                     │ 89650862 │
│ Angelsberg            │ 19165753 │
│ Ana_Sayfa             │  6368793 │
│ Academy_Awards        │  4901276 │
│ Accueil_(homonymie)   │  3805097 │
│ Adolf_Hitler          │  2549835 │
│ 2015_in_spaceflight   │  2077164 │
│ Albert_Einstein       │  1619320 │
│ 19_Kids_and_Counting  │  1430968 │
│ 2015_Nepal_earthquake │  1406422 │
└───────────────────────┴──────────┘

10 rows in set. Elapsed: 2.285 sec. Processed 231.41 million rows, 9.22 GB (101.26 million rows/s., 4.03 GB/s.)
Peak memory usage: 1.50 GiB.

머티어리얼라이즈드 뷰 만들기

다음 머티어리얼라이즈드 뷰를 만들 수 있어요:

CREATE TABLE wikistat_top
(
    `path` String,
    `month` Date,
    hits UInt64
)
ENGINE = SummingMergeTree
ORDER BY (month, hits);
CREATE MATERIALIZED VIEW wikistat_top_mv
TO wikistat_top
AS
SELECT
    path,
    toStartOfMonth(time) AS month,
    sum(hits) AS hits
FROM wikistat
GROUP BY path, month;

대상 테이블 백필링

이 대상 테이블은 새 레코드가 wikistat 테이블에 삽입될 때만 채워지므로, 백필링을 해야 해요. 가장 쉬운 방법은 INSERT INTO SELECT 문을 사용해 뷰의 SELECT 쿼리(변환)를 이용해 머티어리얼라이즈드 뷰의 대상 테이블에 직접 삽입하는 거예요:

INSERT INTO wikistat_top
SELECT
    path,
    toStartOfMonth(time) AS month,
    sum(hits) AS hits
FROM wikistat
GROUP BY path, month;

원시 데이터셋의 카디널리티(10억 행!)에 따라 이는 메모리 집약적 접근 방식일 수 있어요. 대신 메모리가 거의 필요 없는 변형을 사용할 수 있어요:

  • Null 테이블 엔진으로 임시 테이블 만들기
  • 일반적으로 사용하는 머티어리얼라이즈드 뷰의 복사본을 그 임시 테이블에 연결
  • INSERT INTO SELECT 쿼리로 원시 데이터셋의 모든 데이터를 그 임시 테이블로 복사
  • 임시 테이블과 임시 머티어리얼라이즈드 뷰 드롭

이 접근 방식에서 원시 데이터셋의 행은 임시 테이블(어떤 행도 저장하지 않음)로 블록 단위 복사되고, 각 행 블록에 대해 부분 상태가 계산되어 대상 테이블에 기록되며, 그 상태들이 백그라운드에서 증분 병합돼요.

CREATE TABLE wikistat_backfill
(
    `time` DateTime,
    `project` String,
    `subproject` String,
    `path` String,
    `hits` UInt64
)
ENGINE = Null;

다음으로 wikistat_backfill에서 읽어 wikistat_top에 쓰는 머티어리얼라이즈드 뷰를 만들게요:

CREATE MATERIALIZED VIEW wikistat_backfill_top_mv
TO wikistat_top
AS
SELECT
    path,
    toStartOfMonth(time) AS month,
    sum(hits) AS hits
FROM wikistat_backfill
GROUP BY path, month;

그리고 마지막으로 초기 wikistat 테이블에서 wikistat_backfill을 채울게요:

INSERT INTO wikistat_backfill
SELECT *
FROM wikistat;

그 쿼리가 끝나면 백필 테이블과 머티어리얼라이즈드 뷰를 삭제할 수 있어요:

DROP VIEW wikistat_backfill_top_mv;
DROP TABLE wikistat_backfill;

이제 원본 테이블 대신 머티어리얼라이즈드 뷰를 조회할 수 있어요:

SELECT path, sum(hits) AS hits
FROM wikistat_top
WHERE month = '2015-05-01'
GROUP BY ALL
ORDER BY hits DESC
LIMIT 10;
┌─path──────────────────┬─────hits─┐
│ -                     │ 89543168 │
│ Angelsberg            │  7047863 │
│ Ana_Sayfa             │  5923985 │
│ Academy_Awards        │  4497264 │
│ Accueil_(homonymie)   │  2522074 │
│ 2015_in_spaceflight   │  2050098 │
│ Adolf_Hitler          │  1559520 │
│ 19_Kids_and_Counting  │   813275 │
│ Andrzej_Duda          │   796156 │
│ 2015_Nepal_earthquake │   726327 │
└───────────────────────┴──────────┘

10 rows in set. Elapsed: 0.004 sec.

여기서 성능 개선은 극적이에요. 전에는 이 쿼리의 답을 계산하는 데 2초가 조금 넘게 걸렸고, 이제는 4밀리초만 걸려요.

더 알아보기 (Learn more)