ClickHouse의 Superset
ClickHouse를 대상으로 대시보드 재구축: uniqHLL12, quantileTDigest, 샘플링, 윈도 함수, dictGet을 사용하는 7개 데이터셋과 4개 대시보드에 걸친 18개 차트.
이 가이드는 Superset에서 모든 ClickHouse 데이터셋, 차트, 대시보드를 수동으로 설정하는 전 과정을 안내합니다. 각 시각화가 무엇을 하고 어떻게 만들어지는지 이해하려면 이 과정을 따라가세요.
건너뛰고 싶다면? 가져오기 스크립트를 실행하면 모든 것이 자동으로 생성됩니다:
source .env && source .clickhouse_state bash superset/add_clickhouse_connection.sh이 스크립트는 연결을 생성하고, 7개 데이터셋, 18개 차트, 4개 대시보드를 한 번에 가져옵니다. 이 가이드는 참조용으로, 또는 개별 요소를 다시 만들 때 사용하세요.
중요 —
superset/dashboards/dashboard_export_*.zip안의 플레이스홀더 자격 증명.커밋된 대시보드 ZIP은
databases/*.yaml이 플레이스홀더로 마스킹되어 있습니다:sqlalchemy_uri: clickhousedb://default:XXXXXXXXXX@your-instance.clickhouse.cloud:8443/analytics?secure=true
- 자동 가져오기(
add_clickhouse_connection.sh) — 그대로 작동합니다. 스크립트는 가져오기 엔드포인트로 전송하기 전에.env의${CLICKHOUSE_HOST}/${CLICKHOUSE_USER}/${CLICKHOUSE_PASSWORD}를 사용해 ZIP 내부의sqlalchemy_uri를 다시 쓰므로, 플레이스홀더 호스트는 Superset에 절대 도달하지 않습니다.- Superset UI를 통한 수동 가져오기 — 가져온 데이터베이스는
your-instance.clickhouse.cloud로 생성되어 연결되지 않습니다. 가져온 후 Settings → Database Connections → Edit로 이동해 해당 항목의sqlalchemy_uri를 실제 ClickHouse Cloud URI로 교체하세요(예:clickhousedb://default:<PASSWORD>@<your-host>.clickhouse.cloud:8443/analytics?secure=true).- 직접 만든 대시보드를 다시 내보내기 — Superset은 내보내기에 여러분의 실제 ClickHouse 호스트를 그대로 넣습니다. 커밋하기 전에 호스트를
your-instance.clickhouse.cloud로 되돌려 마스킹해서 서비스 식별자가 git 히스토리로 유출되지 않게 하세요.
사전 조건: analytics 계층이 채워져 있고(7.3단계에서 dbt run 완료) 딕셔너리가 존재해야 합니다(7.4단계에서 scripts/04_create_dictionary.sql 완료).
0단계 — ClickHouse 연결 등록
-
http://localhost:8088의 Superset에 로그인합니다 (admin / admin).
-
Settings → Database Connections로 이동합니다.
-
+ Database를 클릭합니다.
-
목록에서 ClickHouse Connect를 선택합니다.
-
다음을 입력합니다:
필드 값 Display Name NYC Taxi — ClickHouse CloudHost ClickHouse Cloud 호스트명 ( .clickhouse_state에서)Port 8443Database analyticsUsername defaultPassword ClickHouse Cloud 비밀번호 SSL 활성화 -
Test Connection을 클릭 — 녹색 성공 배너를 확인합니다.
-
Connect를 클릭합니다.
Part 1 — 데이터셋 생성
데이터셋 1 — fact_trips (테이블 데이터셋)
사용처: Operations Command Center, Executive Weekly Report, Driver Quality Analytics, Capabilities Showcase
- Datasets → + Dataset으로 이동합니다.
- Database =
NYC Taxi — ClickHouse Cloud, Schema =analytics, Table =fact_trips로 설정합니다. - Add Dataset and Create Chart를 클릭 → 그다음 다른 곳으로 이동합니다 — 데이터셋은 저장되었습니다.
데이터셋 2 — agg_hourly_zone_trips (테이블 데이터셋)
사용처: Operations Command Center, Executive Weekly Report
- Datasets → + Dataset으로 이동합니다.
- Database =
NYC Taxi — ClickHouse Cloud, Schema =analytics, Table =agg_hourly_zone_trips로 설정합니다. - Save를 클릭합니다.
데이터셋 3 — CH Approx Unique Trips (uniqHLL12) (가상)
사용처: Capabilities Showcase — 정확한 uniq()와 비교해 uniqHLL12() 근사 카운팅을 보여줍니다.
- Datasets → + Dataset으로 이동합니다.
- Switch to SQL Lab을 클릭합니다(또는 Virtual 탭을 선택합니다).
- Database =
NYC Taxi — ClickHouse Cloud로 설정합니다. - SQL을 붙여넣습니다:
SELECT
toDate(pickup_at) AS day,
uniq(trip_id) AS exact_unique_trips,
uniqHLL12(trip_id) AS approx_unique_trips,
round(
abs(uniq(trip_id) - uniqHLL12(trip_id)) / uniq(trip_id) * 100, 2
) AS pct_error
FROM analytics.fact_trips FINAL
WHERE pickup_at >= today() - INTERVAL 30 DAY
GROUP BY day
ORDER BY day- 이름을 **
CH Approx Unique Trips (uniqHLL12)**로 지정하고 Save를 클릭합니다.
데이터셋 4 — CH Cohort Retention (가상)
사용처: Capabilities Showcase — 자치구별 롤링 매출을 위한 윈도 함수(AVG(...) OVER (...))를 보여줍니다.
- Datasets → + Dataset → Virtual로 이동합니다.
- Database =
NYC Taxi — ClickHouse Cloud로 설정합니다. - SQL을 붙여넣습니다:
SELECT
week,
pickup_borough,
trips,
revenue,
round(avg(revenue) OVER (
PARTITION BY pickup_borough
ORDER BY week
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
), 2) AS rolling_4wk_avg_revenue
FROM (
SELECT
toStartOfWeek(pickup_at) AS week,
pickup_borough,
count() AS trips,
round(sum(fare_amount_usd), 2) AS revenue
FROM analytics.fact_trips FINAL
GROUP BY week, pickup_borough
)
ORDER BY week DESC, revenue DESC
LIMIT 100- 이름을 **
CH Cohort Retention**으로 지정하고 Save를 클릭합니다.
데이터셋 5 — CH Fare Percentiles (quantileTDigest) (가상)
사용처: Capabilities Showcase — ClickHouse 네이티브 백분위 함수로서 quantileTDigest()를 보여줍니다.
- Datasets → + Dataset → Virtual로 이동합니다.
- Database =
NYC Taxi — ClickHouse Cloud로 설정합니다. - SQL을 붙여넣습니다:
SELECT
vendor_name,
quantileTDigest(0.5)(fare_amount_usd) AS p50_fare,
quantileTDigest(0.95)(fare_amount_usd) AS p95_fare,
quantileTDigest(0.99)(fare_amount_usd) AS p99_fare,
count() AS trip_count
FROM analytics.fact_trips FINAL
GROUP BY vendor_name
ORDER BY vendor_name- 이름을 **
CH Fare Percentiles (quantileTDigest)**로 지정하고 Save를 클릭합니다.
데이터셋 6 — CH Sampling Demo (가상)
사용처: Capabilities Showcase — 전체 스캔과 비교해 rand() % N 샘플링의 정확도를 보여줍니다.
- Datasets → + Dataset → Virtual로 이동합니다.
- Database =
NYC Taxi — ClickHouse Cloud로 설정합니다. - SQL을 붙여넣습니다:
SELECT
'Full Scan' AS method,
count() AS trip_count,
round(avg(fare_amount_usd), 4) AS avg_fare
FROM analytics.fact_trips FINAL
UNION ALL
SELECT
'~10% (rand() % 10 = 0)' AS method,
count() * 10 AS trip_count_est,
round(avg(fare_amount_usd), 4) AS avg_fare
FROM analytics.fact_trips FINAL
WHERE rand() % 10 = 0- 이름을 **
CH Sampling Demo**로 지정하고 Save를 클릭합니다.
데이터셋 7 — CH Zone Dict Lookup (가상)
사용처: Capabilities Showcase — JOIN 없는 차원 보강을 위한 dictGet()을 보여줍니다.
필요 조건: 7.4단계의 딕셔너리
analytics.taxi_zones_dict(scripts/04_create_dictionary.sql).
- Datasets → + Dataset → Virtual로 이동합니다.
- Database =
NYC Taxi — ClickHouse Cloud로 설정합니다. - SQL을 붙여넣습니다:
SELECT
dictGet('analytics.taxi_zones_dict', 'borough', toUInt16(pickup_location_id)) AS borough,
dictGet('analytics.taxi_zones_dict', 'zone', toUInt16(pickup_location_id)) AS zone,
count() AS trips,
round(avg(fare_amount_usd), 2) AS avg_fare
FROM default.trips_raw
WHERE pickup_at >= today() - INTERVAL 7 DAY
GROUP BY borough, zone
ORDER BY trips DESC- 이름을 **
CH Zone Dict Lookup**으로 지정하고 Save를 클릭합니다.
이 데이터셋은 사전 처리 없이 소스 계층에서 딕셔너리가 동작하는 것을 보여주기 위해
analytics.fact_trips가 아니라default.trips_raw(원시 테이블)에서 읽습니다.
Part 2 — 차트 생성
Charts → + Chart로 차트를 만들고, 데이터셋을 선택하고, 차트 유형을 고르고, 필드를 구성한 뒤 명시된 이름 그대로 Save하세요.
대시보드 1 — CH Operations Command Center
차트: CH Total Trips Today
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Big Number |
| Metric | COUNT(trip_id) |
| Time filter | pickup_at = today : now |
| Subheader | trips today |
**CH Total Trips Today**로 저장합니다.
차트: CH Revenue Today
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Big Number |
| Metric | SUM(fare_amount_usd) |
| Time filter | pickup_at = today : now |
| Subheader | revenue today |
**CH Revenue Today**로 저장합니다.
차트: CH Trip Volume by Hour (24h)
| 설정 | 값 |
|---|---|
| Dataset | agg_hourly_zone_trips |
| Chart type | Line Chart (ECharts) |
| X-axis | hour_bucket |
| Metric | SUM(trips) |
| Time grain | 1 hour |
**CH Trip Volume by Hour (24h)**로 저장합니다.
차트: CH Revenue by Zone (Top 10)
| 설정 | 값 |
|---|---|
| Dataset | agg_hourly_zone_trips |
| Chart type | Bar Chart |
| Dimensions | zone_id |
| Metric | SUM(revenue) |
| Row limit | 10 |
| Sort bars | 활성화 |
**CH Revenue by Zone (Top 10)**로 저장합니다.
대시보드 2 — CH Executive Weekly Report
차트: CH Daily Revenue (7 days)
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Line Chart (ECharts) |
| X-axis | pickup_at |
| Metric | SUM(fare_amount_usd) |
| Time grain | 1 hour |
**CH Daily Revenue (7 days)**로 저장합니다.
차트: CH Top Zones by Revenue
| 설정 | 값 |
|---|---|
| Dataset | agg_hourly_zone_trips |
| Chart type | Bar Chart |
| Dimensions | zone_id |
| Metric | SUM(revenue) |
| Row limit | 20 |
| Sort bars | 활성화 |
**CH Top Zones by Revenue**로 저장합니다.
차트: CH Payment Distribution
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Pie Chart |
| Dimensions | payment_type |
| Metric | COUNT(trip_id) |
**CH Payment Distribution**으로 저장합니다.
차트: CH Avg Fare by Vendor
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Bar Chart |
| Dimensions | vendor_name |
| Metric | AVG(fare_amount_usd) |
| Row limit | 20 |
| Sort bars | 활성화 |
**CH Avg Fare by Vendor**로 저장합니다.
대시보드 3 — CH Driver Quality Analytics
차트: CH Rating Distribution
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Bar Chart |
| Dimensions | driver_rating |
| Metric | COUNT(trip_id) |
| Row limit | 20 |
| Sort bars | 활성화 |
**CH Rating Distribution**으로 저장합니다.
차트: CH High-Rated Driver Revenue
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Big Number |
| Metric | SUM(fare_amount_usd) |
| Filter | driver_rating >= 4.5 |
| Subheader | revenue from 4.5+ rated drivers |
**CH High-Rated Driver Revenue**로 저장합니다.
차트: CH Avg Fare by Driver Rating
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Bar Chart |
| Dimensions | driver_rating |
| Metric | AVG(fare_amount_usd) |
| Row limit | 20 |
| Sort bars | 활성화 |
**CH Avg Fare by Driver Rating**로 저장합니다.
차트: CH Top Drivers Leaderboard
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Table |
| Query mode | Raw records |
| Columns | vendor_name, vehicle_type, driver_rating, fare_amount_usd |
| Row limit | 1000 |
**CH Top Drivers Leaderboard**로 저장합니다.
대시보드 4 — CH Capabilities Showcase
차트: CH Recent Trips (fact_trips)
| 설정 | 값 |
|---|---|
| Dataset | fact_trips |
| Chart type | Table |
| Query mode | Raw records |
| Columns | trip_id, pickup_at, dropoff_at, fare_amount_usd, pickup_borough, vendor_name |
| Row limit | 1000 |
**CH Recent Trips (fact_trips)**로 저장합니다.
차트: CH Fare Percentiles (quantileTDigest)
| 설정 | 값 |
|---|---|
| Dataset | CH Fare Percentiles (quantileTDigest) |
| Chart type | Table |
| Query mode | Raw records |
| Columns | vendor_name, p50_fare, p95_fare, p99_fare, trip_count |
**CH Fare Percentiles (quantileTDigest)**로 저장합니다.
차트: CH Approx vs Exact Unique Trips (uniqHLL12)
| 설정 | 값 |
|---|---|
| Dataset | CH Approx Unique Trips (uniqHLL12) |
| Chart type | Table |
| Query mode | Raw records |
| Columns | day, exact_unique_trips, approx_unique_trips, pct_error |
**CH Approx vs Exact Unique Trips (uniqHLL12)**로 저장합니다.
차트: CH Sampling Accuracy Demo (SAMPLE 0.1)
| 설정 | 값 |
|---|---|
| Dataset | CH Sampling Demo |
| Chart type | Table |
| Query mode | Raw records |
| Columns | method, trip_count, avg_fare |
**CH Sampling Accuracy Demo (SAMPLE 0.1)**로 저장합니다.
차트: CH Zone Lookup via Dictionary (dictGet)
| 설정 | 값 |
|---|---|
| Dataset | CH Zone Dict Lookup |
| Chart type | Table |
| Query mode | Raw records |
| Columns | borough, zone, trips, avg_fare |
**CH Zone Lookup via Dictionary (dictGet)**로 저장합니다.
차트: CH Weekly Revenue Trend (window functions)
| 설정 | 값 |
|---|---|
| Dataset | CH Cohort Retention |
| Chart type | Table |
| Query mode | Raw records |
| Columns | week, pickup_borough, trips, revenue, rolling_4wk_avg_revenue |
**CH Weekly Revenue Trend (window functions)**로 저장합니다.
Part 3 — 대시보드 조립
각 대시보드마다:
- Dashboards → + Dashboard로 이동합니다.
- 제목을 입력합니다.
- Save를 클릭한 다음 Edit Dashboard를 클릭합니다.
- 오른쪽 패널에서 각 차트를 캔버스로 드래그합니다.
- 완료하면 Save를 클릭합니다.
CH — Operations Command Center
제목: CH — Operations Command Center
| 행 | 차트 |
|---|---|
| 행 1 | CH Total Trips Today · CH Revenue Today |
| 행 2 | CH Trip Volume by Hour (24h) · CH Revenue by Zone (Top 10) |
CH — Executive Weekly Report
제목: CH — Executive Weekly Report
| 행 | 차트 |
|---|---|
| 행 1 | CH Daily Revenue (7 days) · CH Top Zones by Revenue · CH Payment Distribution · CH Avg Fare by Vendor |
CH — Driver Quality Analytics
제목: CH — Driver Quality Analytics
| 행 | 차트 |
|---|---|
| 행 1 | CH Rating Distribution · CH High-Rated Driver Revenue |
| 행 2 | CH Avg Fare by Driver Rating · CH Top Drivers Leaderboard |
CH — Capabilities Showcase
제목: CH — Capabilities Showcase
| 행 | 차트 |
|---|---|
| 행 1 | CH Recent Trips (fact_trips) · CH Fare Percentiles (quantileTDigest) |
| 행 2 | CH Approx vs Exact Unique Trips (uniqHLL12) · CH Sampling Accuracy Demo (SAMPLE 0.1) |
| 행 3 | CH Zone Lookup via Dictionary (dictGet) · CH Weekly Revenue Trend (window functions) |
검증
Part 3을 완료한 후 http://localhost:8088을 엽니다. Dashboards에서 총 7개가 보여야 합니다 — Snowflake 대시보드 3개(Part 1 설정에서)와 CH — 접두사가 붙은 4개입니다.
트러블슈팅
fact_trips 또는 agg_hourly_zone_trips를 찾을 수 없음
먼저 dbt run을 실행하세요(7.3단계).
dictGet이 빈 문자열을 반환함
analytics.taxi_zones_dict 딕셔너리가 생성되지 않았습니다. scripts/04_create_dictionary.sql을 실행하세요(7.4단계).
가상 데이터셋이 행을 반환하지 않음 일부 쿼리의 30일 / 7일 윈도우에는 최근 데이터가 필요합니다. 마이그레이션 데이터가 모두 과거 데이터라면 시간 필터를 고정 날짜로 교체하세요:
-- Replace: WHERE pickup_at >= today() - INTERVAL 30 DAY
-- With: WHERE pickup_at >= '2023-01-01'가져오기 스크립트에서 403 오류 Superset 세션 쿠키가 만료되었습니다. 로그아웃 후 다시 로그인하고 재실행하세요.