Как найти самые популярные элементы в бесконечном потоке событий?
- Что такое count-min sketch?
- Как посчитать топ-100 поисковых запросов за последний час?
- Как найти горячие ключи в потоке запросов?
Count-min sketch — таблица d × w счётчиков и d хеш-функций: при событии увеличиваем по одному счётчику в каждой строке, оценка частоты — минимум из d счётчиков. Только завышает, с ошибкой ≈ поток / w.
- Память фиксированная, независимо от числа разных элементов
- Топ-K: скетч + куча на K элементов с наибольшими оценками
- Альтернатива — Space-Saving / Misra-Gries: K счётчиков, точнее для самых частых
А ещё Скользящее окно: скетчи по минутам, сумма за последние 60; или распределённо — частичные топы с запасом и слияние
Подробно
Задача. Поток из миллиардов событий (запросы, клики, ключи кэша), различных элементов — сотни миллионов. Хранить счётчик на каждый — слишком много памяти. Нужна частота популярных и топ-K.
Count-min sketch. Матрица из d строк по w счётчиков, у каждой строки своя хеш-функция.
- событие x — в каждой строке i увеличить счётчик
hᵢ(x) mod w; - оценка частоты x — минимум из d счётчиков.
Каждый счётчик — это частота x плюс частоты всех, кто попал в ту же ячейку, поэтому оценка никогда не меньше истины. Минимум по строкам выбирает ячейку с наименьшим «мусором». Гарантия: с вероятностью 1 − δ ошибка не больше ε·N, где w = e/ε, d = ln(1/δ). Например, w = 2000, d = 5 — ошибка до 0,14% потока с вероятностью 99%, памяти 10 000 счётчиков. Для редких элементов оценка бесполезна, для частых — точна.
Топ-K. Рядом со скетчем держим мин-кучу на K элементов: после обновления оцениваем x, и если оценка больше минимума кучи — заменяем. Итог — K самых частых с приближёнными частотами.
Space-Saving — K счётчиков; новый элемент при заполнении вытесняет элемент с минимальным счётчиком и наследует его значение + 1. Простая, точная для «тяжёлых» элементов, используется во многих системах мониторинга. Misra-Gries — похожий детерминированный вариант.
Окна времени. «Топ за последний час» — хранить скетчи по минутам, складывать последние 60 (count-min складываются поячеечно) и выкидывать старые. Или затухание: счётчики периодически умножать на коэффициент.
Распределённо. Каждый узел считает свой топ, координатор сливает. Ловушка: элемент может быть 101-м на всех узлах и первым в сумме. Поэтому узлы отдают топ с запасом (например, 10·K) или скетчи целиком — скетчи складываются. Точный топ — пакетно: MapReduce, ClickHouse topK (приближённый) или точная агрегация по ночам.
Применения: популярные запросы и тренды, горячие ключи кэша (W-TinyLFU в Caffeine использует count-min как частотный фильтр), обнаружение DDoS по источникам, тяжёлые пользователи API.
Пример
import random
class CountMin:
def __init__(self, w=2000, d=5, seed=1):
rnd = random.Random(seed)
self.w, self.rows = w, [[0] * w for _ in range(d)]
self.salts = [rnd.getrandbits(64) for _ in range(d)]
def add(self, x, c=1):
for row, s in zip(self.rows, self.salts):
row[hash((s, x)) % self.w] += c
def estimate(self, x):
return min(row[hash((s, x)) % self.w] for row, s in zip(self.rows, self.salts))Что хотят услышать
- Почему точный подсчёт дорог.
- Устройство count-min: матрица, d хешей, минимум; только завышение.
- Топ-K через кучу; Space-Saving как альтернатива.
- Окна времени и слияние распределённых топов.
Частые ошибки
- Считать, что count-min может недооценить частоту.
- Сливать распределённые топ-K без запаса.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Count-min sketch занижает частоты
- ✗Память скетча растёт с числом разных элементов
- ✓Count-min sketch, только завышающий частоту, плюс куча на K
- ✗Count-min sketch хранит все элементы потока
Что спросят дальше
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.