쿼리 성능
쿼리 성능 (Query performance)
시계열 워크로드의 쿼리 성능을 ORDER BY 키 최적화와 머티어리얼라이즈드 뷰로 개선하는 방법을 살펴봐요.
출처: 문서
본문
스토리지를 최적화한 다음 단계는 쿼리 성능 개선이에요. 이 섹션은 두 가지 핵심 기법을 탐구해요: ORDER BY 키 최적화와 머티어리얼라이즈드 뷰 사용. 이러한 접근 방식이 쿼리 시간을 초 단위에서 밀리초 단위로 줄이는 방법을 볼 수 있어요.
ORDER BY 키 최적화
다른 최적화를 시도하기 전에 ClickHouse가 가능한 가장 빠른 결과를 만들도록 정렬 키를 최적화해야 해요. 올바른 키 선택은 실행할 쿼리에 크게 의존해요. 대부분의 쿼리가 project와 subproject 컬럼으로 필터링한다고 가정해요. 이 경우 정렬 키에 추가하는 것이 좋아요 — 시간으로도 조회하므로 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밀리초만 걸려요.