Требования: топ-100 поисковых запросов за последний час (обновление раз в минуту) и за день; допустима небольшая неточность для часового; чтение топа — тысячи раз в секунду. Оценка: 100 000 запросов в секунду, сотни миллионов разных строк в день.
Ввод: события поиска → Kafka. Предварительно — нормализация (регистр, пробелы, синонимы, отсев ботов и мусора).
Быстрая дорожка:
- партиционирование событий по хешу запроса — все события одного запроса попадают к одному агрегатору, и его счётчик точен в пределах узла (иначе нужно сливать частичные топы с запасом);
- агрегатор держит счётчики за текущую минуту. Если различных ключей слишком много — count-min sketch + мин-куча на K (или Space-Saving на несколько тысяч элементов);
- раз в минуту агрегатор отдаёт свой локальный топ (с запасом, например 1000) и закрывает минутную корзину;
- сервис слияния хранит 60 минутных топов каждого агрегатора, складывает по ключам и берёт топ-100 за час. Поскольку ключи разделены по агрегаторам, слияние — просто объединение;
- результат — в Redis/кэш:
top:hour — готовый список.
Медленная дорожка: сырые события → ClickHouse/хранилище; раз в час и раз в сутки точный GROUP BY query ORDER BY count DESC LIMIT 100 (или topK). Дневной топ берётся отсюда; часовой можно сверять.
Горячие ключи: один запрос («погода») может давать заметную долю трафика и перегрузить свой агрегатор. Предагрегирование на приёмниках (локальные счётчики за секунду) снижает число событий в разы.
Тренды — не самые частые, а самые выросшие: сравнение частоты в текущем окне с базовой (вчера в это же время, среднее за неделю) с порогом и сглаживанием, иначе в трендах всегда «погода» и «ютуб».
Отказы: агрегатор упал — перечитать партицию с последнего зафиксированного смещения; состояние окна можно восстанавливать из снимков (Flink, Kafka Streams делают это сами).