Learning Platform
Глоссарий Troubleshooting
Урок 06.04 · 20 мин
Средний
SAMPLE BYApproximate QueriesintHash32_sample_factorDeterministic Sampling

SAMPLE BY: приблизительные запросы

Таблица с миллиардами строк. Нужна приблизительная статистика — средний чек, количество уникальных пользователей, распределение событий. Точный ответ требует сканирования всей таблицы. Приблизительный — достаточно прочитать 1-10% данных и экстраполировать.

ClickHouse поддерживает SAMPLE BY — встроенный механизм детерминированной выборки. В отличие от случайной выборки (RAND()), SAMPLE BY всегда возвращает одни и те же строки при одинаковой доле — результаты воспроизводимы.


Объявление SAMPLE BY в DDL

SAMPLE BY определяется при создании таблицы. Три обязательных требования:

  1. Выражение SAMPLE BY должно входить в ORDER BY — ClickHouse использует физическую сортировку для эффективной выборки
  2. Выражение должно возвращать unsigned integer — обычно intHash32 или intHash64
  3. Хеш-функция обеспечивает равномерное распределение — intHash32 превращает любой столбец в равномерно распределённые значения
CREATE TABLE events (
    event_date Date,
    user_id UInt64,
    event_type String,
    revenue Float64
) ENGINE = MergeTree()
ORDER BY (intHash32(user_id), event_date)
SAMPLE BY intHash32(user_id);
WARNING

SAMPLE BY выражение должно быть частью ORDER BY ключа. Если ORDER BY не содержит SAMPLE BY выражение, ClickHouse вернёт ошибку при создании таблицы. Обратите внимание: intHash32(user_id) стоит первым в ORDER BY — это обеспечивает физическую группировку данных по хеш-значениям для эффективной выборки.


Три формы SAMPLE в запросах

SAMPLE fraction — доля от 0 до 1

-- Прочитать примерно 10% данных
SELECT avg(revenue), count()
FROM events
SAMPLE 0.1;

-- 1% данных
SELECT uniq(user_id)
FROM events
SAMPLE 0.01;

SAMPLE 0.1 означает: прочитать ~10% строк таблицы. ClickHouse выбирает строки, где intHash32(user_id) попадает в определённый диапазон значений. Поскольку intHash32 распределяет равномерно, ~10% строк будет выбрано.

SAMPLE N — абсолютное количество строк

-- Прочитать примерно 1 000 000 строк
SELECT avg(revenue)
FROM events
SAMPLE 1000000;

ClickHouse автоматически вычислит необходимую долю исходя из общего количества строк.

SAMPLE 1/N — дробная форма

-- Эквивалент SAMPLE 0.1
SELECT count()
FROM events
SAMPLE 1/10;

Детерминированность

Критическое свойство SAMPLE BY — детерминированность. Один и тот же запрос с одним и тем же SAMPLE всегда возвращает одни и те же строки.

-- Оба запроса вернут идентичные строки
SELECT user_id FROM events SAMPLE 0.1;
SELECT user_id FROM events SAMPLE 0.1;

Это возможно потому, что выборка основана на хеш-значении (intHash32(user_id)), а не на случайном числе. Строки с хеш-значениями в диапазоне [0, MAX_UINT32 * 0.1] всегда одни и те же.

Практическое следствие: можно сравнивать результаты приблизительных запросов во времени. Если SAMPLE 0.1 SELECT count() WHERE status='ERROR' показал 150 вчера и 200 сегодня — это реальный рост ошибок, а не случайное отклонение выборки.


Экстраполяция с _sample_factor

Виртуальный столбец _sample_factor содержит коэффициент выборки (обратную долю). Для SAMPLE 0.01 значение _sample_factor = 100.

-- Приблизительное общее количество событий
SELECT count() * _sample_factor AS estimated_total
FROM events
SAMPLE 0.01;

-- Приблизительная сумма дохода
SELECT sum(revenue) * _sample_factor AS estimated_revenue
FROM events
SAMPLE 0.1;
TIP

_sample_factor полезен для агрегатных функций count() и sum(). Для avg() экстраполяция не нужна — среднее по выборке и есть приближение среднего по всей таблице. Для uniq() и quantile() экстраполяция также не нужна.


Когда использовать SAMPLE BY

Хорошие сценарии:

  • Дашборды реального времени на таблицах с миллиардами строк — 1-10% выборка даёт достаточную точность за доли секунды
  • Exploratory analytics — быстрый анализ паттернов перед написанием точного запроса
  • A/B тестирование — SAMPLE BY intHash32(user_id) даёт стабильные когорты пользователей

Плохие сценарии:

  • Точные финансовые отчёты — приблизительные значения неприемлемы
  • Фильтры с низкой кардинальностью в WHERE — выборка может пропустить все записи определённого значения
  • Таблицы менее 100 000 строк — полный скан и так быстр

Полный пример

-- DDL: таблица аналитики с SAMPLE BY
CREATE TABLE analytics (
    event_date Date,
    user_id UInt64,
    action String,
    duration_ms UInt32
) ENGINE = MergeTree()
ORDER BY (intHash32(user_id), event_date)
SAMPLE BY intHash32(user_id);

-- Вставка данных
INSERT INTO analytics VALUES
    ('2024-01-15', 1001, 'page_view', 250),
    ('2024-01-15', 1002, 'click', 50),
    ('2024-01-15', 1003, 'page_view', 300);

-- Приблизительное количество уникальных пользователей
-- (10% выборка вместо полного сканирования)
SELECT uniq(user_id) AS approx_users
FROM analytics
SAMPLE 0.1;

-- Экстраполяция общего количества действий
SELECT
    action,
    count() * _sample_factor AS estimated_count,
    avg(duration_ms) AS avg_duration
FROM analytics
SAMPLE 0.1
GROUP BY action;

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

  1. SAMPLE BY — встроенный механизм детерминированной выборки. Одна и та же доля всегда возвращает одни и те же строки.
  2. DDL: SAMPLE BY выражение должно входить в ORDER BY и возвращать unsigned integer. Типичная формула: intHash32(column).
  3. Три формы: SAMPLE 0.1 (доля), SAMPLE 1000000 (абсолютное число строк), SAMPLE 1/10 (дробь).
  4. _sample_factor — виртуальный столбец для экстраполяции: count() * _sample_factor даёт приблизительное общее количество.
  5. Используйте для дашбордов и exploratory analytics на больших таблицах. Не используйте для точных финансовых отчётов.
Приближенные алгоритмы в Spark: sampling, sketches, reservoir Streaming: event time, watermarks, windows

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 3. Какой тип должно возвращать выражение SAMPLE BY в DDL таблицы ClickHouse?

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

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

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

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