Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Счетчики онлайн событий Основы Redis

Развитие любого веб-сайта происходит на основе аналитических данных. Одна из часто используемых метрик — количество посетителей страницы. Иногда эту метрику показывают пользователям в интерфейсе. Как лучше реализовать такой счетчик на бэкенде?

storage

Сперва можно попробовать хранить записи о посещаемости в таблице реляционной базы данных. Структура таблицы visits будет следующей: идентификатор с автоинкрементом, абсолютная ссылка на страницу, количество посещений и дата. На связку (url, date) нужно добавить уникальный индекс, чтобы не было дублей в счетчиках.

traffic_table

Когда пользователь открывает страницу, выполняется запрос

INSERT INTO visits (url, count, date) VALUES ('hexlet.io/courses', 1, '2022-01-01') ON DUPLICATE KEY UPDATE count = count + 1

Такой запрос является атомарным и будет правильно выполняться в конкурентной среде. Если на веб-странице низкая посещаемость, то данное решение сработает. Однако, что если 1000 пользователей зайдут на страницу в один момент времени? Счетчик в базе увеличится на 1000. С консистентностью все хорошо, но что будет с производительностью? Классические базы данных тратят примерно 15% времени на полезную работу непосредственно с запросом. Все остальное время тратится на блокировки и управление транзакциями в конкурентной среде. Из-за блокировок транзакции обновление счетчика будут выполняться последовательно. Если у первого пользователя запрос выполнится за 20 мс, то у второго уже за 40 мс, и так далее. Причем рост задержки ответа не будет линейным. То есть 500-ый пользователь будет ждать не 500 * 20мс = 10 000 мс (10с), а намного больше. Естественно, ни один бэкенд не будет ждать более пары секунд выполнения запроса в БД и просто отвалится по таймауту. Итого, при нагрузке в 1000 одновременных запросов теряется ~90% данных, что естественно недопустимо.

response_time

Атомарные инкременты в Redis

Счетчики — отличный кейс использования Redis, и сейчас разберемся почему.

Инкремент

Представим, что на платформе необходимо отображать количество онлайн пользователей в режиме реального времени. Для этого можно хранить счетчик по ключу page:{url}:online_count. При подключении нового пользователя выполняется увеличение счетчика на 1 (инкремент) с помощью команды incr:

127.0.0.1:6379> incr page:hexlet.io/courses:online_count
(integer) 1
127.0.0.1:6379> incr page:hexlet.io/courses:online_count
(integer) 2
127.0.0.1:6379> incr page:hexlet.io/courses:online_count
(integer) 3

На каждый запрос возвращается новое значение после увеличения.

Также существует возможность увеличить счетчик на N за раз с командой incrby:

127.0.0.1:6379> incrby page:hexlet.io/courses:online_count 27
(integer) 30

Декремент

В определенный момент пользователь закрывает страницу, и необходимо уменьшить счетчик онлайна. Используется команда decr:

127.0.0.1:6379> decr page:hexlet.io/courses:online_count
(integer) 29

Как и с увеличением есть команда для уменьшения счетчика на N:

127.0.0.1:6379> decrby page:hexlet.io/courses:online_count 19
(integer) 10

Взаимодействие со счетчиком

Со счетчиком можно работать так же, как и с обычным ключом: получать значение, устанавливать время жизни, удалять:

127.0.0.1:6379> get page:hexlet.io/courses:online_count
"10"
127.0.0.1:6379> expire page:hexlet.io/courses:online_count 60
(integer) 1
127.0.0.1:6379> ttl page:hexlet.io/courses:online_count
(integer) 53
127.0.0.1:6379> del page:hexlet.io/courses:online_count
(integer) 1

Резюме

  • в простых проектах онлайн-счетчик можно реализовать с помощью классических РСУБД
  • онлайн-счетчики имеют простую структуру данных и небольшое количество идентификаторов (ключей), что отлично ложится на архитектуру хранения в оперативной памяти, в частности в Redis
  • Redis отлично справляется с консистентностью данных в высоконагруженной конкурентной среде, потому что все запросы выполняются атомарно, последовательно и за пару миллисекунд

Дополнительные материалы

  1. Redis INCR command
  2. Redis INCRBY command
  3. Redis DECR command
  4. Redis DECRBY command

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»
Изображение Тото

Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу