Архитектуры на ClickHouse: PostHog, Cloudflare, GitLab
ClickHouse используется в production у сотен компаний. Три крупнейших публичных case study — PostHog, Cloudflare и GitLab — демонстрируют повторяющиеся архитектурные паттерны: широкие плоские таблицы, Kafka для ingestion, агрессивная денормализация и TTL для управления жизненным циклом данных.
PostHog: шардированный кластер для product analytics
PostHog — open-source продуктовая аналитика с само-хостингом. Их ClickHouse-кластер хранит события поведения пользователей в масштабе.
Архитектура:
- Шардированный кластер: несколько узлов с горизонтальным масштабированием
- Kafka pull model: PostHog использует Kafka engine в ClickHouse для pull-чтения из Kafka-топиков (не push через HTTP). Materialized view записывает данные в основные таблицы
- ReplacingMergeTree для профилей пользователей: person profiles требуют дедупликации — при обновлении свойств пользователя вставляется новая версия, старая вытесняется при merge
- Агрессивная дедупликация:
OPTIMIZE TABLE FINALиспользуется на небольших таблицах; для production масштаба —SELECT ... FINALили дедупликация через приложение
-- Kafka engine для pull ingestion (PostHog-like паттерн)
CREATE TABLE events_kafka_queue
(
uuid String,
team_id UInt32,
event String,
properties String, -- JSON-строка
timestamp DateTime64(3)
)
ENGINE = Kafka()
SETTINGS
kafka_broker_list = 'kafka:9092',
kafka_topic_list = 'clickhouse_events',
kafka_group_name = 'clickhouse-consumer',
kafka_format = 'JSONEachRow';
-- Основная таблица событий (широкая, денормализованная)
CREATE TABLE events
(
uuid UUID,
team_id UInt32,
event LowCardinality(String),
distinct_id String,
properties String,
timestamp DateTime64(3),
-- Денормализованные свойства для быстрых фильтров
browser LowCardinality(String) MATERIALIZED
JSONExtractString(properties, '$browser'),
os LowCardinality(String) MATERIALIZED
JSONExtractString(properties, '$os'),
country LowCardinality(String) MATERIALIZED
JSONExtractString(properties, '$geoip_country_code')
)
ENGINE = ReplacingMergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (team_id, toDate(timestamp), event, uuid)
TTL toDate(timestamp) + INTERVAL 2 YEAR;
-- Materialized view: Kafka queue -> основная таблица
CREATE MATERIALIZED VIEW events_mv TO events AS
SELECT
toUUID(uuid) AS uuid,
team_id,
event,
JSONExtractString(properties, 'distinct_id') AS distinct_id,
properties,
timestamp
FROM events_kafka_queue;
-- Person profiles: ReplacingMergeTree для дедупликации по версии
CREATE TABLE person
(
team_id UInt32,
id UUID,
created_at DateTime64(3),
properties String, -- JSON-строка с атрибутами
version UInt64 -- монотонно возрастает при каждом update
)
ENGINE = ReplacingMergeTree(version)
ORDER BY (team_id, id);
Паттерн multi-tenancy через RBAC: Каждый PostHog team_id изолируется через CREATE ROW POLICY — tenant видит только свои данные.
Cloudflare: 6 миллионов запросов в секунду
Cloudflare использует ClickHouse для bot management, customer dashboards и real-time network analytics. Масштаб: более 6M RPS на пике.
Ключевые архитектурные решения:
- Columnar performance: ClickHouse обрабатывает HTTP-запросы с признаками (IP-адрес, User-Agent, заголовки, геолокация) в одной широкой строке. Колоночное хранение позволяет сканировать только нужные атрибуты бота
- Real-time customer dashboards: Клиенты Cloudflare видят свой трафик с задержкой менее 1 минуты через pre-aggregated materialized views
- Многоуровневый MergeTree: Горячие данные (последние 24 часа) на быстрых NVMe SSD, архивные данные в S3 через tiered storage (JBOD + S3)
-- Упрощённая схема запроса HTTP-событий (Cloudflare-like)
CREATE TABLE http_requests
(
-- Ключевые измерения
zone_id UInt32,
client_ip IPv4,
ray_id UInt64,
request_time DateTime64(3),
-- Bot detection features (денормализованы для скорости)
user_agent String,
cf_threat_score UInt8,
cf_bot_score UInt8,
is_bot UInt8,
-- Геолокация
country_code LowCardinality(String),
region LowCardinality(String),
-- HTTP метаданные
method LowCardinality(String),
status_code UInt16,
bytes_sent UInt32,
edge_server_id UInt16
)
ENGINE = MergeTree()
PARTITION BY toStartOfHour(request_time)
ORDER BY (zone_id, toDate(request_time), client_ip)
TTL request_time + INTERVAL 30 DAY;
-- Pre-aggregated view для customer dashboard (sub-second latency)
CREATE MATERIALIZED VIEW zone_hourly_stats
ENGINE = SummingMergeTree()
ORDER BY (zone_id, hour, country_code)
AS SELECT
zone_id,
toStartOfHour(request_time) AS hour,
country_code,
count() AS total_requests,
sum(bytes_sent) AS total_bytes,
countIf(is_bot = 1) AS bot_requests,
countIf(status_code >= 400) AS error_requests
FROM http_requests
GROUP BY zone_id, hour, country_code;
GitLab: sub-second queries для миллионов пользователей
GitLab — крупнейшая платформа DevOps с само-хостингом и GitLab.com. ClickHouse используется для CI/CD аналитики и метрик разработчиков.
Масштаб: Десятки миллионов пользователей, миллиарды CI/CD событий, требования к задержке ниже 1 секунды для большинства dashboard-запросов.
Архитектурные решения:
- CI/CD аналитика: Данные о pipeline jobs, stage timings, test results — в ClickHouse вместо PostgreSQL. PostgreSQL не справлялся с аналитическими запросами при таком масштабе
- Широкие таблицы: Pipeline job денормализован (включает данные о project, namespace, runner, stage) — исключает JOIN на горячем пути
- Async_insert для высокого throughput: GitLab.com генерирует тысячи CI-событий в секунду;
async_insert=1буферизует вставки и снижает нагрузку на ClickHouse
-- Упрощённая схема CI/CD событий (GitLab-like)
CREATE TABLE ci_builds
(
build_id UInt64,
pipeline_id UInt64,
project_id UInt32,
namespace_id UInt32,
-- Денормализованные атрибуты проекта
project_path String,
namespace_path String,
-- Характеристики job
stage_name LowCardinality(String),
job_name String,
status LowCardinality(String), -- success/failed/canceled
runner_type LowCardinality(String), -- shared/group/project
-- Временные метки
created_at DateTime,
started_at DateTime,
finished_at DateTime,
duration_seconds Float32 MATERIALIZED
if(finished_at > started_at,
dateDiff('second', started_at, finished_at),
NULL)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (project_id, created_at, build_id)
TTL created_at + INTERVAL 3 YEAR;
-- Запрос: среднее время сборки по проекту за последние 30 дней
SELECT
project_path,
stage_name,
avg(duration_seconds) AS avg_duration,
quantile(0.95)(duration_seconds) AS p95_duration,
countIf(status = 'failed') AS failed_count,
count() AS total_count
FROM ci_builds
WHERE created_at >= now() - INTERVAL 30 DAY
AND status IN ('success', 'failed')
GROUP BY project_path, stage_name
ORDER BY avg_duration DESC
LIMIT 20;
Общие паттерны production-архитектур
Повторяющиеся паттерны во всех трёх архитектурах:
Широкие плоские таблицы: Вместо 3NF нормализации — денормализованные события с повторяющимися атрибутами. ClickHouse компрессирует повторяющиеся значения лучше, чем JOIN-операции при каждом запросе.
-- Anti-pattern: JOIN на горячем пути запроса
SELECT e.event, u.country, p.name
FROM events e
JOIN users u ON e.user_id = u.id
JOIN projects p ON e.project_id = p.id
WHERE e.ts >= now() - INTERVAL 1 HOUR;
-- ClickHouse pattern: денормализованная таблица, нет JOIN
SELECT event, user_country, project_name
FROM events_denormalized
WHERE ts >= now() - INTERVAL 1 HOUR;
Kafka engine pull model: PostHog и аналоги используют Kafka engine (ClickHouse тянет из Kafka) вместо Kafka Connect (внешний процесс пушит в ClickHouse). Преимущество: нет дополнительной инфраструктуры, backpressure управляется ClickHouse.
MergeTree + TTL для data lifecycle: Все три компании используют TTL для автоматического удаления старых данных. Исключает ручные DELETE-мутации на больших таблицах.
async_insert для высокого throughput: При вставках с клиентских агентов (особенно при высоком числе параллельных источников) async_insert=1 буферизует вставки и снижает число частей на диске.
Ключевые выводы
-
PostHog демонстрирует стандартный паттерн product analytics: Kafka pull model через Kafka engine + MV, ReplacingMergeTree для mutable entity profiles, RBAC для multi-tenancy.
-
Cloudflare (6M RPS) доказывает: columnar storage ClickHouse эффективнее row-based DB для real-time analytics с высокой кардинальностью. Pre-aggregated MV обеспечивают sub-second customer dashboards.
-
GitLab показывает сценарий миграции: PostgreSQL заменён ClickHouse только для аналитических запросов (CI/CD analytics), OLTP данные остались в PostgreSQL — правильное разделение ответственности.
-
Широкие плоские таблицы + агрессивная денормализация — универсальный паттерн. ClickHouse компрессирует повторяющиеся значения в колонках эффективно; JOIN overhead при каждом запросе дороже дублирования данных.
-
Kafka + async_insert + TTL — тройная основа production-архитектур с высоким throughput: Kafka для надёжного ingestion, async_insert для сглаживания пиков, TTL для автоматического жизненного цикла данных.