Сэмплирование данных

Иногда ответ «примерно» по 10% данных в десять раз быстрее точного по всем.

Сэмплирование (sampling) — вычисление приблизительного результата по детерминированной части данных вместо всей таблицы ради скорости.

Зачем считать «примерно»

Для многих задач точность до последней цифры не нужна. «Сколько примерно уникальных пользователей зашло» или «какова приблизительная конверсия» — для дашборда хватит оценки с погрешностью в доли процента, зато в разы быстрее. Сэмплирование читает, скажем, каждую десятую строку и масштабирует результат.

Как это включить

Сэмплирование задаётся на этапе создания таблицы — через SAMPLE BY с колонкой из первичного ключа (обычно хешированной), а в запросе используется SAMPLE:

CREATE TABLE events
(
    event_date Date,
    user_id    UInt32,
    value      Float64
)
ENGINE = MergeTree
ORDER BY (event_date, intHash32(user_id))
SAMPLE BY intHash32(user_id);

-- Запрос по 10% данных
SELECT count() * 10 AS approx_total
FROM events
SAMPLE 0.1;

Здесь SAMPLE 0.1 берёт ~10% данных; чтобы оценить полное число, результат домножают на 10.

Важное свойство: детерминированность

Сэмплирование в ClickHouse стабильно: одна и та же выборка попадает в семпл всегда. Поэтому, если семплировать по user_id, в выборку попадут все события одних и тех же пользователей. Это позволяет корректно считать показатели вроде «событий на пользователя» — пользователь либо целиком в выборке, либо целиком вне её.

Что можно и нельзя оценивать

МетрикаСэмплирование
Доли, конверсии, средниеоценивается хорошо
Приблизительные суммы/счётчикиоценивается (масштабированием)
Точные финансовые итогине годится — нужна точность
Редкие события («хвосты»)опасно — могут не попасть в семпл

Иллюстрация масштабирования

Покажем идею оценки полного объёма по семплу (чистый Python):

sample_fraction = 0.1
count_in_sample = 12345        # столько строк попало в 10%-семпл
estimated_total = count_in_sample / sample_fraction
print("В семпле строк:", count_in_sample)
print("Оценка всего:", int(estimated_total))

Вывод:

В семпле строк: 12345
Оценка всего: 123450

Как работает под капотом

ClickHouse использует SAMPLE BY-колонку как «координату»: значение хеша делит пространство ключей, и SAMPLE 0.1 отбирает первую десятую часть этого пространства. Поскольку отбор идёт по детерминированному хешу, он стабилен и читает только соответствующие гранулы — отсюда ускорение.

Частые ошибки

  • Сэмплировать финансы. Для точных денежных сумм приближение недопустимо.
  • Забыть масштабировать. Сумма/счётчик по семплу — это доля; её нужно домножить на обратную долю семпла.
  • Искать редкие события в семпле. Редкие случаи могут вовсе не попасть в выборку.

Итоги

  • Сэмплирование считает приблизительно по части данных — ради скорости.
  • Требует SAMPLE BY в схеме; в запросе — SAMPLE долю.
  • Выборка детерминирована и группирует все строки одного ключа вместе.
  • Подходит для долей/средних, но не для точных финансов и редких событий.
Проверьте себя
1. Когда сэмплирование уместно?
AДля точного расчёта зарплат и налогов
BДля быстрой приблизительной оценки долей, конверсий и средних
CДля удаления данных
DВсегда вместо обычных запросов
2. Почему сэмплирование в ClickHouse детерминированно (по user_id)?
AЧтобы каждый раз брать случайные строки
BЧтобы все события одного пользователя попадали в выборку вместе — это корректно для метрик на пользователя
CЧтобы удвоить объём данных
DЭто побочный эффект, пользы нет