Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 09.09 · 35 мин
Продвинутый
uniquniqCombineduniqExactuniqHLL12uniqThetaquantilequantileTDigestquantileExactquantileTimingquantileBFloat16Approximate FunctionsHyperLogLog

Приближенные функции: uniq и quantile семейства

ClickHouse предоставляет два семейства приближенных функций: uniq (оценка кардинальности — сколько уникальных значений) и quantile (оценка перцентилей — какое значение на позиции N%). Приближенные функции жертвуют абсолютной точностью ради радикального снижения потребления памяти и времени вычисления. На миллиардах строк разница между точным и приближенным результатом — секунды vs минуты, мегабайты vs гигабайты.


Семейство uniq: оценка кардинальности

Семейство uniq: 6 функций для подсчёта уникальных значений
uniq -- адаптивныйuniq(col) -- адаптивный алгоритм (HyperLogLog + sampling). Погрешность ~1-2%. Малый расход памяти. Рекомендация: дефолтный выбор для аналитики, где допустима 2% погрешность.
uniqCombined -- гибридuniqCombined(col) -- гибридный алгоритм (Array + Hash + HLL12). Погрешность ~1%. Средний расход памяти. Рекомендация: когда нужен -State/-Merge для MV/AggregatingMergeTree.
uniqHLL12 -- 2.5 КБuniqHLL12(col) -- HyperLogLog с 12-bit регистрами. Погрешность ~1.5%. Фиксированный размер состояния ~2.5 КБ. Рекомендация: когда нужен предсказуемый размер состояния.
uniqCombined64 -- 64-bituniqCombined64(col) -- как uniqCombined, но с 64-bit hash. Погрешность ~1%. Рекомендация: для высокой кардинальности (более 100M уникальных значений), где 32-bit hash даёт коллизии.
uniqExact -- точныйuniqExact(col) -- полный hash set. Погрешность 0% (точный). Память: пропорциональна кардинальности (неограниченна). Рекомендация: только когда бизнес требует абсолютно точный подсчёт.
uniqTheta -- set opsuniqTheta(col) -- Theta Sketch. Погрешность ~1%. Уникальная возможность: поддержка set-операций (union, intersect, not). Рекомендация: когда нужны пересечения/объединения множеств уникальных значений.

Сравнительная таблица

ФункцияАлгоритмПогрешностьПамятьКогда использовать
uniqАдаптивный (HLL)~1-2%МалаяДефолтный выбор для аналитических дашбордов
uniqCombinedArray+Hash+HLL12~1%СредняяНужен -State/-Merge для MV
uniqCombined64Array+Hash+HLL12 (64-bit)~1%СредняяКардинальность более 100M
uniqHLL12HyperLogLog 12-bit~1.5%2.5 КБПредсказуемый размер состояния
uniqExactПолный hash set0%НеограниченнаяТочный подсчёт обязателен
uniqThetaTheta Sketch~1%МалаяSet-операции (intersect/union)

Примеры использования

-- Дефолтный выбор: приближенный подсчёт уникальных пользователей
SELECT
    toDate(event_time) AS day,
    uniq(user_id) AS unique_users
FROM events
GROUP BY day
ORDER BY day;
-- Быстро, ~1-2% погрешность, минимум памяти
-- Точный подсчёт: когда бизнес требует абсолютной точности
-- (AB-тестирование, биллинг, compliance-отчёты)
SELECT
    experiment_id,
    variant,
    uniqExact(user_id) AS exact_users
FROM ab_test_events
GROUP BY experiment_id, variant;
-- Медленнее, RAM пропорциональна кардинальности
-- Set-операции: пересечение сегментов через uniqTheta
SELECT
    uniqTheta(user_id) AS segment_a_users
FROM events WHERE segment = 'premium';

-- Пересечение: пользователи И premium, И active
SELECT uniqThetaIntersect(sketch_a, sketch_b)
FROM (
    SELECT
        uniqThetaState(user_id) AS sketch_a
    FROM events WHERE segment = 'premium'
) a, (
    SELECT
        uniqThetaState(user_id) AS sketch_b
    FROM events WHERE segment = 'active'
) b;

Семейство quantile: оценка перцентилей

Перцентили — ключевой инструмент для анализа распределений: P50 (медиана), P95 (95-й перцентиль), P99 (99-й перцентиль).

Сравнительная таблица

ФункцияАлгоритмСкоростьТочностьОграничения
quantileReservoir samplingБыстраяПриближеннаяНедетерминированная
quantileTDigestT-DigestСредняяХорошая (~1%)
quantileExactПолная сортировкаМедленнаяТочнаяRAM = все значения
quantileTimingФиксированные бакетыОчень быстраяХорошаяМаксимум 30 000 (30 секунд)
quantileDeterministicДетерминированный samplingБыстраяПриближеннаяРезультат повторяемый
quantileBFloat16BFloat16 квантизацияОчень быстраяНизкаяДля быстрой оценки

quantile — дефолтный выбор

-- P50 (медиана), P95, P99 для латентности запросов
SELECT
    quantile(0.5)(query_duration_ms) AS p50,
    quantile(0.95)(query_duration_ms) AS p95,
    quantile(0.99)(query_duration_ms) AS p99
FROM system.query_log
WHERE type = 'QueryFinish';

Результат недетерминированный — при повторном запросе может незначительно отличаться (reservoir sampling использует случайную выборку).


quantileTDigest — баланс скорости и точности

-- P99 латентности API по эндпоинтам
SELECT
    endpoint,
    quantileTDigest(0.99)(latency_ms) AS p99_latency
FROM api_logs
GROUP BY endpoint
ORDER BY p99_latency DESC
LIMIT 10;

T-Digest обеспечивает ~1% относительную погрешность для крайних перцентилей (P95, P99) и лучшую точность для медианы. Состояние фиксированного размера — не зависит от количества данных.


quantileTiming — для латентностей

-- Очень быстрый P99 для сетевых латентностей
SELECT
    service,
    quantileTiming(0.99)(response_time_ms) AS p99
FROM requests
GROUP BY service;
WARNING

quantileTiming принимает значения от 0 до 30 000 (интерпретируемые как миллисекунды, максимум 30 секунд). Значения больше 30 000 приравниваются к 30 000. Если ваши данные могут превышать 30 секунд, используйте quantileTDigest.


quantileExact — абсолютная точность

-- Точная медиана зарплат (финансовые отчёты)
SELECT
    department,
    quantileExact(0.5)(salary) AS median_salary
FROM employees
GROUP BY department;
-- Хранит все значения в памяти, затем сортирует

Использовать только когда бизнес-требования запрещают приближение (финансы, compliance). На миллиардах строк потребление памяти может быть значительным.


Фреймворк выбора функции

Выбор uniq-функции

  1. Нужна точность 0%? -> uniqExact (проверьте RAM-бюджет)
  2. Нужны set-операции (intersect/union)? -> uniqTheta
  3. Кардинальность более 100M? -> uniqCombined64
  4. Нужен -State/-Merge для MV? -> uniqCombined
  5. Нужен фиксированный размер состояния? -> uniqHLL12
  6. Все остальные случаи -> uniq (дефолт)

Выбор quantile-функции

  1. Нужна точность 100%? -> quantileExact
  2. Данные — латентности до 30 секунд? -> quantileTiming (самая быстрая)
  3. Нужна точность на хвостах (P95/P99)? -> quantileTDigest
  4. Нужен детерминированный результат? -> quantileDeterministic
  5. Все остальные случаи -> quantile (дефолт)

Множественные перцентили за один проход

ClickHouse поддерживает вычисление нескольких перцентилей одним вызовом функции:

-- Вместо 3 отдельных quantile():
SELECT
    quantiles(0.5, 0.95, 0.99)(latency_ms) AS percentiles
FROM requests;
-- Возвращает Array: [p50, p95, p99]

-- Аналогично для T-Digest:
SELECT
    quantilesTDigest(0.5, 0.95, 0.99)(latency_ms) AS percentiles
FROM requests;

Это эффективнее, чем три отдельных вызова — данные проходятся один раз.


Комбинаторы с приближенными функциями

Все uniq- и quantile-функции поддерживают комбинаторы из урока 01:

-- uniqIf: подсчёт уникальных только среди платящих
SELECT uniqIf(user_id, plan = 'paid') AS paying_users
FROM users;

-- quantileTDigestIf: P99 только для ошибок
SELECT quantileTDigestIf(0.99)(latency_ms, status_code >= 500) AS error_p99
FROM api_logs;

-- uniqState/uniqMerge: для MV + AggregatingMergeTree
-- (подробнее в уроке 01 о комбинаторах)

Ключевые выводы

  1. uniq — дефолтный выбор для подсчёта уникальных значений (~1-2% погрешность, минимум памяти). uniqExact — только когда бизнес требует точности.
  2. uniqTheta — единственный вариант, поддерживающий set-операции (intersect/union) на sketch-уровне.
  3. quantileTDigest — лучший баланс точности и скорости для P95/P99. quantileTiming — самая быстрая, но ограничена 30 секундами.
  4. quantiles()/quantilesTDigest() — множественные перцентили за один проход данных.
  5. Фреймворк выбора: точность -> ограничения -> память -> скорость. Начинайте с приближенной функции, переходите к точной только по требованию бизнеса.
BtrBlocks: каскадное кодирование с вероятностными структурами Streaming: event time, watermarks, windows

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 10