Подій у веб-додатку стає більше 100 мільйонів, і PostgreSQL починає видавати результат за хвилини. Ми стикалися з цим не раз: клієнт просить звіт по DAU за 90 днів із розбивкою за джерелами, а запит зависає на 40 секунд. ClickHouse — колонкова СУБД, розроблена Яндексом для аналітичних навантажень. Вона обробляє мільярди рядків за секунди. Наш досвід впровадження ClickHouse у 10+ проєктах за 5 років показує стабільне прискорення в 10–100x. ClickHouse не замінює транзакційну базу — це доповнення: PostgreSQL для операційних даних, ClickHouse для аналітики. Ми гарантуємо: після налаштування ви забудете про тайм-аути.
ClickHouse дає економію бюджету на серверах у 5–10 разів.
Переваги ClickHouse для аналітики веб-додатків
ClickHouse використовує колонкове зберігання: дані кожної колонки лежать окремо — запит читає лише потрібні стовпці. Векторизована обробка дозволяє CPU виконувати операції над пачками значень, а не над окремими рядками. Однотипні дані стискаються в 5–10 разів ефективніше, ніж у PostgreSQL. ClickHouse пропонує спеціалізовані двигуни таблиць, такі як MergeTree, які оптимізують зберігання та запити для аналітичних сценаріїв.
MergeTree: пристрій та оптимізація
MergeTree — основний двигун ClickHouse. Він сортує дані за ключем ORDER BY і розбиває на гранули по 8192 рядки. При запиті ClickHouse відсікає цілі гранули на основі первинного індексу та додаткових індексів, таких як bloom_filter. Це дає pruning на рівні блоків, що значно скорочує обсяг сканованих даних.
Як проектувати схему таблиць?
CREATE TABLE events (
event_date Date,
event_time DateTime,
event_type LowCardinality(String),
user_id UInt64,
session_id String,
tenant_id UInt32,
page_url String,
referrer String,
country LowCardinality(String),
device_type LowCardinality(String),
properties String
) ENGINE = MergeTree()
ORDER BY (tenant_id, event_date, event_type, user_id)
PARTITION BY toYYYYMM(event_date);
ALTER TABLE events ADD INDEX idx_session session_id TYPE bloom_filter(0.01) GRANULARITY 4;
ORDER BY — ключ сортування, за яким ClickHouse зберігає дані. Запити з фільтрами по tenant_id та event_date використовують його для pruning. LowCardinality — оптимізація для колонок з малою кількістю унікальних значень (~10k), зберігає як dictionary encoding.
Ефективна вставка даних у ClickHouse
ClickHouse оптимізований під пакетну вставку. Використовуйте буферизацію, як у прикладі нижче.
import { createClient } from '@clickhouse/client';
const client = createClient({
host: process.env.CLICKHOUSE_HOST,
username: process.env.CLICKHOUSE_USER,
password: process.env.CLICKHOUSE_PASSWORD,
database: 'analytics',
});
class EventBuffer {
private buffer: EventRow[] = [];
private flushTimer: NodeJS.Timeout;
async push(event: EventRow) {
this.buffer.push(event);
if (this.buffer.length >= 1000) await this.flush();
}
async flush() {
if (!this.buffer.length) return;
const rows = [...this.buffer];
this.buffer = [];
await client.insert({
table: 'events',
values: rows,
format: 'JSONEachRow',
});
}
}
Ніколи не вставляйте по одному рядку — для високих навантажень використовуйте Kafka Engine.
Як прискорити запити?
Ось приклад аналітичних запитів, які виконуються за секунди на 100M рядків.
-- DAU за 90 днів
SELECT event_date, uniqExact(user_id) AS dau
FROM events
WHERE tenant_id = 42 AND event_date >= today() - 90 AND event_type = 'pageview'
GROUP BY event_date
ORDER BY event_date;
-- Воронка конверсії
SELECT
countIf(event_type = 'product_view') AS views,
countIf(event_type = 'add_to_cart') AS cart,
countIf(event_type = 'checkout_start') AS checkout,
countIf(event_type = 'purchase') AS purchases,
round(100.0 * purchases / views, 2) AS conversion_pct
FROM events
WHERE tenant_id = 42 AND event_date BETWEEN '2024-01-01' AND '2024-01-31';
uniqExact — точний підрахунок унікальних. uniq — наближений (~2% похибка), на порядок швидше.
Порівняння швидкості: PostgreSQL vs ClickHouse
| Запит | PostgreSQL (100M рядків) | ClickHouse (100M рядків) | Прискорення |
|---|---|---|---|
| DAU за 90 днів | 42 сек | 0.4 сек | ~100x |
| Воронка конверсії за квартал | 18 сек | 0.2 сек | ~90x |
| Когортний аналіз | 35 сек | 0.6 сек | ~58x |
Materialized Views: автоматична предагрегація
CREATE MATERIALIZED VIEW daily_metrics
ENGINE = SummingMergeTree()
ORDER BY (tenant_id, event_date, country, device_type)
AS SELECT
tenant_id,
event_date,
country,
device_type,
count() AS events_count,
uniqState(user_id) AS unique_users_state,
uniqState(session_id) AS unique_sessions_state
FROM events
GROUP BY tenant_id, event_date, country, device_type;
SELECT
event_date,
sum(events_count) AS total_events,
uniqMerge(unique_users_state) AS unique_users
FROM daily_metrics
WHERE tenant_id = 42 AND event_date >= today() - 7
GROUP BY event_date;
Матеріалізовані представлення автоматично оновлюються при вставці та зберігають предагреговані метрики. Це прискорює типові звіти в десятки разів.
Інтеграція та експлуатація
Інтеграція з Laravel та Node.js
Для Laravel використовуйте пакет sanchov/laravel-clickhouse. Додайте з'єднання clickhouse в config/database.php та виконуйте запити як: DB::connection('clickhouse')->select(..., [$tenantId]). Для Node.js — офіційний @clickhouse/client, приклад вставки вище.
Реплікація та TTL
CREATE TABLE events ON CLUSTER analytics_cluster (
...
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}')
ORDER BY (tenant_id, event_date, event_type, user_id)
PARTITION BY toYYYYMM(event_date)
TTL event_date + INTERVAL 2 YEAR DELETE;
TTL автоматично видаляє дані старші за два роки. Для compliance можна налаштувати переміщення в холодне сховище.
Типові помилки та процес роботи
Типові помилки
Найчастіша помилка — спроба вставляти дані по одному рядку. ClickHouse не призначений для транзакційних вставок. Друга — неправильний вибір ключа сортування ORDER BY. Якщо фільтр по user_id та event_date, то порядок в ORDER BY має відповідати частоті фільтрації. Третя — забути про Materialized Views для типових звітів, що призводить до повного сканування таблиці.
Що входить у роботу
- Аналіз поточної моделі даних та метрик
- Проектування схеми events + Materialized Views
- Налаштування реплікації та TTL
- Код інтеграції з Laravel або Node.js
- Документація схеми та запитів
- Навчання команди (1–2 години)
- Підтримка 1 місяць після впровадження
Етапи та терміни
| Етап | Термін | Вартість |
|---|---|---|
| Схема events + Materialized Views + інтеграція з Laravel | 1–2 тижні | індивідуально |
| Когортний аналіз, retention, реплікація, Kafka Engine | 2–4 тижні | індивідуально |
Замовте безкоштовний аудит вашої поточної аналітичної схеми — наш інженер з 10-річним досвідом проаналізує вузькі місця. Щоб обговорити деталі, зв'яжіться з нами через форму на сайті. Окупність проєкту — 3–6 місяців за рахунок зниження витрат на інфраструктуру.







