Как спроектировать систему подсчёта просмотров видео в приложении
Что проверяют
Понимаете ли вы, что счётчик популярного видео это горячая строка, а точный подсчёт в реальном времени стоит дорого. Уточняют обычно, как считать уникальные просмотры и как не засчитать накрутку.
Как отвечать
Уточните, что считается просмотром (старт, 30 секунд, досмотр), нужна ли точность до единицы и с какой задержкой цифра должна обновляться. Потом схема:
- приём событий: клиент шлёт событие в лёгкий эндпоинт на FastAPI, тот валидирует его и кладёт в Kafka или Redis Streams. Никакого
UPDATE videos SET views = views + 1на каждый запрос; - агрегация: консьюмер копит события в памяти и раз в несколько секунд делает пачечный инкремент:
INCRBYпо ключу видео в Redis или одинUPDATEна пачку в PostgreSQL; - горячие ключи: счётчик вирусного видео делят на N подсчётчиков со случайным суффиксом и суммируют при чтении;
- уникальные: точный подсчёт требует хранить пары пользователь и видео. Для приблизительного хватит HyperLogLog:
PFADDиPFCOUNTв Redis дают погрешность около 0,81% при максимум 12 КБ на ключ; - история и отчёты: сырые события в ClickHouse или в партиционированной по дням таблице, оттуда графики и пересчёт;
- антифрод: дедупликация по пользователю и устройству в окне времени, лимиты по IP, отсев ботов. Публичную цифру можно показывать после фильтрации.
Пользователь видит значение из кеша, поэтому оно отстаёт на секунды, и для продукта это нормально. Чтобы событие не терялось, консьюмер подтверждает сообщение только после записи агрегата.
Где спотыкаются
Инкрементируют счётчик в основной базе синхронно на каждый просмотр. На одном популярном ролике строка блокируется, запросы выстраиваются в очередь.
Путают общее число просмотров с уникальными и не могут объяснить, как посчитать вторые, не храня каждого зрителя.
Забывают про повторную доставку: при семантике at-least-once без идемпотентной обработки счётчик растёт от ретраев, а не от зрителей.