이 예시에서는 materialized view를 생성한 다음, 첫 번째 materialized view를 기반으로 두 번째 materialized view를 캐스케이딩하는 방법을 설명합니다. 이 페이지에서는 구현 방법, 다양한 활용 가능성, 그리고 제한 사항을 살펴봅니다. 두 번째 Materialized view를 소스로 사용하는 Materialized view를 생성하면 다양한 사용 사례를 해결할 수 있습니다.
예시:
도메인 이름 그룹의 시간당 조회 수를 담은 가상의 데이터셋을 사용하겠습니다.
목표
- 각 도메인 이름별로 월 단위 집계 데이터가 필요합니다.
- 각 도메인 이름별로 연 단위 집계 데이터도 필요합니다.
다음 옵션 중 하나를 선택할 수 있습니다.
- SELECT 요청 시 데이터를 읽고 집계하는 쿼리를 작성합니다.
- 수집 시점에 데이터를 새로운 포맷으로 준비합니다.
- 수집 시점에 데이터를 특정 집계 형태로 준비합니다.
Materialized view를 사용해 데이터를 준비하면 ClickHouse가 처리해야 하는 데이터 양과 계산량을 줄일 수 있으므로 SELECT 요청을 더 빠르게 수행할 수 있습니다.
materialized view용 소스 테이블
소스 테이블을 생성합니다. 목표는 개별 행이 아니라 집계된 데이터에 대한 보고이므로, 데이터를 파싱한 뒤 정보를 Materialized Views로 전달하고 실제로 들어오는 데이터는 버릴 수 있습니다. 이는 목표에 부합할 뿐 아니라 스토리지도 절약할 수 있으므로 Null 테이블 엔진을 사용합니다.
CREATE DATABASE IF NOT EXISTS analytics;CREATE TABLE analytics.hourly_data
(
`domain_name` String,
`event_time` DateTime,
`count_views` UInt64
)
ENGINE = Null월별 집계 테이블 및 materialized view
첫 번째 materialized view에서는 먼저 Target 테이블을 생성해야 합니다. 이 예시에서는 analytics.monthly_aggregated_data를 사용하며, 월별 도메인 이름 기준 조회수 합계를 저장합니다.
CREATE TABLE analytics.monthly_aggregated_data
(
`domain_name` String,
`month` Date,
`sumCountViews` AggregateFunction(sum, UInt64)
)
ENGINE = AggregatingMergeTree
ORDER BY (domain_name, month)대상 테이블로 데이터를 전달하는 materialized view는 다음과 같습니다:
CREATE MATERIALIZED VIEW analytics.monthly_aggregated_data_mv
TO analytics.monthly_aggregated_data
AS
SELECT
toDate(toStartOfMonth(event_time)) AS month,
domain_name,
sumState(count_views) AS sumCountViews
FROM analytics.hourly_data
GROUP BY
domain_name,
month연도별 집계 테이블과 materialized view
이제 앞서 만든 대상 테이블(target table) monthly_aggregated_data에 연결될 두 번째 Materialized view를 생성하겠습니다.
먼저 각 도메인 이름별로 연도 단위로 집계한 조회 수 합계를 저장할 새 대상 테이블(target table)을 생성하겠습니다.
CREATE TABLE analytics.year_aggregated_data
(
`domain_name` String,
`year` UInt16,
`sumCountViews` UInt64
)
ENGINE = SummingMergeTree()
ORDER BY (domain_name, year)이 단계에서 캐스케이딩을 정의합니다. FROM 문은 monthly_aggregated_data 테이블을 사용하므로, 데이터 흐름은 다음과 같습니다.
- 데이터가
hourly_data테이블로 들어옵니다. - ClickHouse는 수신한 데이터를 첫 번째 materialized view인
monthly_aggregated_data테이블로 전달합니다. - 마지막으로 2단계에서 수신한 데이터가
year_aggregated_data테이블로 전달됩니다.
CREATE MATERIALIZED VIEW analytics.year_aggregated_data_mv
TO analytics.year_aggregated_data
AS
SELECT
toYear(toStartOfYear(month)) AS year,
domain_name,
sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data
GROUP BY
domain_name,
year예제 데이터
이제 데이터를 삽입하여 캐스케이딩 materialized view를 테스트해 보겠습니다:
INSERT INTO analytics.hourly_data (domain_name, event_time, count_views)
VALUES ('clickhouse.com', '2019-01-01 10:00:00', 1),
('clickhouse.com', '2019-02-02 00:00:00', 2),
('clickhouse.com', '2019-02-01 00:00:00', 3),
('clickhouse.com', '2020-01-01 00:00:00', 6);analytics.hourly_data의 내용을 SELECT하면 테이블 엔진이 Null이기 때문에 데이터는 처리되었지만 다음과 같이 표시됩니다.
SELECT * FROM analytics.hourly_dataOk.
0 rows in set. Elapsed: 0.002 sec.예상한 결과와 비교하며 흐름을 쉽게 확인할 수 있도록 소규모 데이터셋을 사용했습니다. 작은 데이터셋으로 흐름이 올바르게 작동하는 것이 확인되면, 이후에는 대량의 데이터로 진행할 수 있습니다.
결과
sumCountViews 필드를 선택해 대상 테이블을 쿼리하면, 값이 숫자가 아니라 AggregateFunction 타입으로 저장되기 때문에 이진 표현이 표시됩니다(일부 터미널에서).
집계의 최종 결과를 얻으려면 -Merge 접미사를 사용해야 합니다.
다음 쿼리를 사용하면 AggregateFunction에 저장된 특수 문자를 확인할 수 있습니다:
SELECT sumCountViews FROM analytics.monthly_aggregated_data┌─sumCountViews─┐
│ │
│ │
│ │
└───────────────┘
3 rows in set. Elapsed: 0.003 sec.대신 sumCountViews 값을 가져오기 위해 Merge 접미사를 사용해 보겠습니다:
SELECT
sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data;┌─sumCountViews─┐
│ 12 │
└───────────────┘
1 행 in set. Elapsed: 0.003 sec.AggregatingMergeTree에서는 AggregateFunction을 sum으로 정의했으므로 sumMerge를 사용할 수 있습니다. AggregateFunction에 avg 함수를 사용한 경우에는 avgMerge를 사용하며, 다른 함수도 마찬가지입니다.
SELECT
month,
domain_name,
sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data
GROUP BY
domain_name,
month이제 materialized view가 앞서 정의한 목표를 충족하는지 살펴보겠습니다.
이제 데이터가 대상 테이블 monthly_aggregated_data에 저장되었으므로, 각 도메인 이름별 월간 집계 데이터를 가져올 수 있습니다:
SELECT
month,
domain_name,
sumMerge(sumCountViews) AS sumCountViews
FROM analytics.monthly_aggregated_data
GROUP BY
domain_name,
month┌──────month─┬─domain_name────┬─sumCountViews─┐
│ 2020-01-01 │ clickhouse.com │ 6 │
│ 2019-01-01 │ clickhouse.com │ 1 │
│ 2019-02-01 │ clickhouse.com │ 5 │
└────────────┴────────────────┴───────────────┘
3 rows in set. Elapsed: 0.004 sec.각 도메인 이름에 대해 연도별로 집계한 데이터:
SELECT
year,
domain_name,
sum(sumCountViews)
FROM analytics.year_aggregated_data
GROUP BY
domain_name,
year┌─year─┬─domain_name────┬─sum(sumCountViews)─┐
│ 2019 │ clickhouse.com │ 6 │
│ 2020 │ clickhouse.com │ 6 │
└──────┴────────────────┴────────────────────┘
2 rows in set. Elapsed: 0.004 sec.여러 소스 테이블을 하나의 대상 테이블로 결합하기
materialized view는 여러 소스 테이블을 동일한 대상 테이블로 결합하는 데에도 사용할 수 있습니다. 이는 UNION ALL과 유사한 로직의 materialized view를 생성할 때 유용합니다.
먼저, 서로 다른 메트릭 집합을 나타내는 두 개의 소스 테이블을 생성합니다:
CREATE TABLE analytics.impressions
(
`event_time` DateTime,
`domain_name` String
) ENGINE = MergeTree ORDER BY (domain_name, event_time)
;
CREATE TABLE analytics.clicks
(
`event_time` DateTime,
`domain_name` String
) ENGINE = MergeTree ORDER BY (domain_name, event_time)
;그런 다음 결합된 메트릭 집합을 사용해 Target 테이블을 생성합니다:
CREATE TABLE analytics.daily_overview
(
`on_date` Date,
`domain_name` String,
`impressions` SimpleAggregateFunction(sum, UInt64),
`clicks` SimpleAggregateFunction(sum, UInt64)
) ENGINE = AggregatingMergeTree ORDER BY (on_date, domain_name)동일한 Target 테이블을 대상으로 하는 materialized view 2개를 생성합니다. 누락된 컬럼은 명시적으로 포함하지 않아도 됩니다:
CREATE MATERIALIZED VIEW analytics.daily_impressions_mv
TO analytics.daily_overview
AS
SELECT
toDate(event_time) AS on_date,
domain_name,
count() AS impressions,
0 clicks ---<<<--- 생략해도 동일하게 0이 됩니다
FROM
analytics.impressions
GROUP BY
toDate(event_time) AS on_date,
domain_name
;
CREATE MATERIALIZED VIEW analytics.daily_clicks_mv
TO analytics.daily_overview
AS
SELECT
toDate(event_time) AS on_date,
domain_name,
count() AS clicks,
0 impressions ---<<<--- 생략해도 동일하게 0이 됩니다
FROM
analytics.clicks
GROUP BY
toDate(event_time) AS on_date,
domain_name
;이제 값을 삽입하면 해당 값이 Target 테이블의 각 컬럼에 맞게 집계됩니다:
INSERT INTO analytics.impressions (domain_name, event_time)
VALUES ('clickhouse.com', '2019-01-01 00:00:00'),
('clickhouse.com', '2019-01-01 12:00:00'),
('clickhouse.com', '2019-02-01 00:00:00'),
('clickhouse.com', '2019-03-01 00:00:00')
;
INSERT INTO analytics.clicks (domain_name, event_time)
VALUES ('clickhouse.com', '2019-01-01 00:00:00'),
('clickhouse.com', '2019-01-01 12:00:00'),
('clickhouse.com', '2019-03-01 00:00:00')
;결합된 노출 수와 클릭 수가 Target 테이블에 함께 저장됩니다:
SELECT
on_date,
domain_name,
sum(impressions) AS impressions,
sum(clicks) AS clicks
FROM
analytics.daily_overview
GROUP BY
on_date,
domain_name
;이 쿼리를 실행하면 다음과 비슷한 결과가 출력됩니다:
┌────on_date─┬─domain_name────┬─impressions─┬─clicks─┐
│ 2019-01-01 │ clickhouse.com │ 2 │ 2 │
│ 2019-03-01 │ clickhouse.com │ 1 │ 1 │
│ 2019-02-01 │ clickhouse.com │ 1 │ 0 │
└────────────┴────────────────┴─────────────┴────────┘
3 rows in set. Elapsed: 0.018 sec.