Как возвращать счётчики просмотров для видео в ленте
Что проверяют
Умеете ли вы разделить путь записи и путь чтения для счётчика под большой нагрузкой и договориться о допустимой точности. Следом обычно спрашивают, что будет с вирусным видео и как считать уникальные просмотры.
Как отвечать
Начните с уточнений: сколько видео в одной выдаче ленты, насколько свежим должен быть счётчик и что считается просмотром. Для ленты почти всегда достаточно значения с отставанием в десятки секунд, пользователь всё равно видит «1,2 тыс.».
Путь записи:
- атомарный инкремент: событие просмотра уходит в Redis командой
INCR video:{id}:views, она атомарна и не требует блокировок; - буферизация: при большом потоке события пишут в Kafka, воркер агрегирует их за окно и раз в несколько секунд делает один
UPDATE videos SET views = views + %sна видео; - источник истины: итоговое значение сбрасывается в PostgreSQL, чтобы потеря Redis не обнулила счётчики.
Путь чтения:
- батч: лента из 30 видео забирает счётчики одним
MGETили через pipeline вredis.asyncio, а не тридцатью запросами; - денормализация:
viewsлежит колонкой в карточке видео или прямо в закешированном ответе ленты; - промах кеша: при отсутствии ключа значение берётся из базы и прогревает Redis.
В FastAPI это обычный асинхронный эндпоинт ленты, где счётчики подмешиваются к карточкам после выборки. Для уникальных зрителей подходит HyperLogLog: PFADD и PFCOUNT дают оценку с погрешностью около 0.81% и занимают до 12 КБ на ключ.
Где спотыкаются
Считают COUNT(*) по таблице просмотров на каждое чтение или делают отдельный запрос на каждое видео ленты. Это классический N+1, который упадёт первым.
Делают UPDATE ... SET views = views + 1 в PostgreSQL на каждый просмотр. Популярное видео становится горячей строкой с очередью блокировок, а таблица обрастает мёртвыми версиями строк. Ещё хуже читать значение в Python, прибавлять единицу и записывать обратно: параллельные запросы теряют инкременты.
Не определяют, что такое просмотр. Без порога по времени и дедупликации по пользователю счётчик накручивается ботами и повторными заходами.