Как рассчитать нагрузку в rps и задать нефункциональные требования к производительности в точных цифрах: калькуляция на примере интернет-магазина.
Описание контекста
Требования к производительности системы – одни из самых важных НФТ (нефункциональных требований), которые необходимо определить в техническом задании (ТЗ), т.к. они в большей степени влияют на выбор архитектурных решений. Если система не справится с нагрузкой, пострадает бизнес, а, значит, цель автоматизации не будет достигнута. Часто в ТЗ требования к производительности системы задаются в количестве одновременно работающих пользователей и частоте запросов от них в единицу времени.
Однако, для проектирования хранилища данных важно понимать, какие виды запросов (на чтение или на запись) будут преобладать. Это позволит принять решение о необходимости разделять единое хранилище на несколько разных, реализуя архитектурный паттерн CQRS (Command and Query Responsibility Segregation). Такое разделение запросов на чтение и команд на мутации данных позволяет улучшить масштабируемость системы при высоких нагрузках, ее быстродействие и повысить безопасность благодаря дополнительным проверкам при мутациях. Однако, для сохранения целостности данных нужно обеспечить быструю синхронизацию изменений между разными хранилищами. Это повышает архитектурную сложность системы и стоимость ее эксплуатации. Впрочем, архитектурное проектирование – это всегда поиск компромисса.
Эта диаграмма создана с помощью следующего PlantUML-скрипта:
@startuml title CQRS (Command and Query Responsibility Segregation) actor Пользователь rectangle "Система" { Пользователь --> (Изменить данные) : команда Пользователь --> (Прочитать данные) : запрос database "БД на запись" AS WDB (Изменить данные) --> WDB database "БД на чтение" AS RDB (Прочитать данные) --> RDB WDB -> RDB : Обновление } @enduml
Чтобы понять необходимость применения CQRS-паттерна для независимой оптимизации моделей чтения и записи данных, надо оценить нагрузки на эти операции в числовом выражении. Для этого вернемся к производительности и рассмотрим пример расчета нагрузки для интернет-магазина.
Сначала надо определить сценарии работы с данными и частоту выполнения каждого из них. Имея в ТЗ реестр вариантов использования (ВИ) системы, сделать первое довольно просто. Например, пользовательскими ВИ для интернет-магазина будут Посмотреть каталог товаров, Найти товар, Посмотреть товар, Добавить товар в корзину, Оформить заказ, Посмотреть историю заказов, Найти заказ, Посмотреть заказ, Изменить заказ. Еще появятся системные ВИ, связанные с регистрацией и аутентификацией пользователя, а также обновлением данных в клиентском профиле.
Таблица 1. Реестр ВИ
№ | ВИ | Актор |
UC-1 | Посмотреть каталог товаров | Любой пользователь |
UC-2 | Найти товар | Любой пользователь |
UC-3 | Посмотреть товар | Любой пользователь |
UC-4 | Добавить товар в корзину | Любой пользователь |
UC-5 | Оформить заказ | Зарегистрированный клиент |
UC-6 | Посмотреть историю заказов | Зарегистрированный клиент |
UC-7 | Найти заказ | Зарегистрированный клиент |
UC-8 | Посмотреть заказ | Зарегистрированный клиент |
UC-9 | Изменить заказ | Зарегистрированный клиент |
UC-10 | Войти в систему | Любой пользователь |
UC-11 | Регистрация | Любой пользователь |
UC12- | Аутентификация | Зарегистрированный клиент |
UC-13 | Обновить профиль | Зарегистрированный клиент |
Эта диаграмма создана с помощью следующего PlantUML-скрипта:
@startuml skinparam packageStyle rectangle actor Пользователь as U actor Клиент as C C --|> U rectangle "Интернет-магазин" { U -> (Войти в систему) U ---> (Посмотреть каталог товаров) U --> (Найти товар) U --> (Посмотреть товар) U -> (Добавить товар в корзину) C --> (Посмотреть историю заказов) C --> (Посмотреть заказ) C -> (Обновить профиль) (Найти заказ) ..> (Посмотреть историю заказов) : extend (Посмотреть заказ) <.. (Изменить заказ) : extend (Аутентификация) <.. (Посмотреть заказ) : include (Посмотреть каталог товаров) <. (Найти товар) : extend (Добавить товар в корзину) <. (Оформить заказ) : extend (Добавить товар в корзину) ..> (Посмотреть товар): include (Посмотреть товар) ...> (Найти товар): include (Оформить заказ) .> (Аутентификация) : include (Посмотреть заказ) ..> (Найти заказ): include (Войти в систему) <. (Регистрация) : extend (Войти в систему) <.. (Аутентификация) : extend (Обновить профиль) ..> (Аутентификация) : include } @enduml
Расчет нагрузки
Теперь выделим из реестра ВИ сценарии на чтение и на запись данных. К операциям на чтение относятся Посмотреть каталог товаров, Найти товар, Посмотреть товар, Посмотреть историю заказов, Найти заказ, Посмотреть заказ. К операциям на запись относятся Добавить товар в корзину, Оформить заказ, Изменить заказ, Регистрация, Обновить профиль. Вход в систему и Аутентификация будем рассматривать как операции чтения, поскольку они включают в себя доступ к уже существующей информации без её изменения. При аутентификации система считывает введенные пользователем данные (например, логин и пароль) и проверяет, что они соответствуют хранящейся в базе данных информации. Этот процесс не вносит никаких изменений в данные пользователя, а только проверяет их достоверность, выполняя доступ к данным без их изменения. Хотя на практике в процессе аутентификации могут выполняться операции записи, связанные с созданием пользовательской сессии и логированием, для упрощения примера будем считать, что ВИ аутентификации ориентирован на чтение данных. Отметим это в таблице ВИ.
Таблица 2. Реестр ВИ с определением видов операций
№ | ВИ | Актор | Вид операции |
UC-1 | Посмотреть каталог товаров | Любой пользователь | Чтение |
UC-2 | Найти товар | Любой пользователь | Чтение |
UC-3 | Посмотреть товар | Любой пользователь | Чтение |
UC-4 | Добавить товар в корзину | Любой пользователь | Запись |
UC-5 | Оформить заказ | Зарегистрированный клиент | Запись |
UC-6 | Посмотреть историю заказов | Зарегистрированный клиент | Чтение |
UC-7 | Найти заказ | Зарегистрированный клиент | Чтение |
UC-8 | Посмотреть заказ | Зарегистрированный клиент | Чтение |
UC-9 | Изменить заказ | Зарегистрированный клиент | Запись |
UC-10 | Войти в систему | Любой пользователь | Чтение |
UC-11 | Регистрация | Любой пользователь | Запись |
UC-12 | Аутентификация | Зарегистрированный клиент | Чтение |
UC-13 | Обновить профиль | Зарегистрированный клиент | Запись |
Чтобы вычислить частоту выполнения каждого ВИ, нужна статистика пользовательской активности. Если она отсутствует, придется делать допущения. Предположим, в нашем магазине есть следующие значения метрик и показателей:
- 50 000 клиентов, т.е. зарегистрированных пользователей;
- DAU (Daily Active Users), ежедневное количество активных пользователей = 10 000, из них 70% — это зарегистрированные пользователи;
- ежедневно регистрируется 500 новых клиентов;
- количество входов в систему в день = 20 000, т.е. по 2 сессии на пользователя в день;
- среднее количество операций за сессию = 15;
- средняя длительность пользовательской сессии = 10 минут;
- пиковое количество одновременно работающих пользователей = 1 000;
- основная активность пользователей происходит после рабочего дня, с 18:00 до 21:00 МСК;
- пользователи просматривают каталог товаров 50 000 раз за день;
- количество поисковых запросов по товарам 30 000 в день;
- отдельные товары просматриваются 40 000 раз в день;
- просмотр истории заказов выполняется около 500 раз в день;
- поиск по заказам запрашивается примерно 1000 раз в день;
- отдельные заказы просматриваются не более 800 раз в день;
- в среднем в заказе 5 товаров;
- ежедневно оформляется 5 000 заказов;
- ежедневно отменяется или изменяется 1 000 заказов;
- около 2 000 обновлений в день происходит с данными пользовательских профилей.
Таблица 3. Реестр ВИ с определением частоты выполнения операций
№ | ВИ | Актор | Вид операции | Частота
(количество в день) |
UC-1 | Посмотреть каталог товаров | Любой пользователь | Чтение | 50 000 |
UC-2 | Найти товар | Любой пользователь | Чтение | 30 000 |
UC-3 | Посмотреть товар | Любой пользователь | Чтение | 40 000 |
UC-4 | Добавить товар в корзину | Любой пользователь | Запись | 25 000 |
UC-5 | Оформить заказ | Зарегистрированный клиент | Запись | 5 000 |
UC-6 | Посмотреть историю заказов | Зарегистрированный клиент | Чтение | 500 |
UC-7 | Найти заказ | Зарегистрированный клиент | Чтение | 1 000 |
UC-8 | Посмотреть заказ | Зарегистрированный клиент | Чтение | 800 |
UC-9 | Изменить заказ | Зарегистрированный клиент | Запись | 1 000 |
UC-10 | Войти в систему | Любой пользователь | Чтение | 20 000 |
UC-11 | Регистрация | Любой пользователь | Запись | 500 |
UC-12 | Аутентификация | Зарегистрированный клиент | Чтение | 20 000 |
UC-13 | Обновить профиль | Зарегистрированный клиент | Запись | 2 000 |
Посчитаем количество операций на чтение в день:
- Посмотреть каталог товаров = 50 000;
- Найти товар = 30 000;
- Посмотреть товар = 40,000;
- Вход в систему и аутентификация = 20 000;
- Посмотреть историю заказов = 500;
- Найти заказ = 1 000;
- Посмотреть заказ = 800.
Итого в день выполняется 142 300 операций на чтение:
50 000 + 30 000 + 40 000 + 20 000 + 500 + 1 000 + 800 = 142 300
Теперь выполним аналогичный расчет количества операций на запись (в день):
- Добавить товар в корзину = 25 000;
- Оформить заказ = 5 000;
- Изменить заказ = 1 000;
- Регистрация = 500;
- Обновить профиль = 2 000
Итого в день выполняется 33 500 операций на запись:
25 000 + 5 000 + 1 000 + 500 + 2 000 = 33 500
Для расчёта нагрузки в запросах в секунду (rps, request per second) необходимо разделить количество операций на чтение и запись, выполняемых за день, на количество секунд в сутках (86400 секунд). Итого получим
- 142 300 / 86 400 ≈ 1,64 rps на чтение;
- 33 500 / 86 400 ≈ 0,39 rps на запись;
Это означает, что каждую секунду имеем 1,64 запросов на чтение и 0,39 запросов на запись. Таким образом, примерно 98 запросов на чтение и 23 запроса на запись в минуту. Это довольно низкая нагрузка, которая не требует сложных архитектурных решений, таких как CQRS, которое разделяет операции на команды, изменяющие состояние системы, и запросы, читающие данные. Поскольку текущие нагрузки очень низкие, применение CQRS избыточно и неоправданно. Подробнее об этом расскажу в следующей статье. А в заключение еще раз подчеркну важность количественного определения НФТ для качественного проектирования и точной реализации.
Как именно выявить и описать в ТЗ требования, достаточные проектирования и разработки информационных систем, вы узнаете на моих курсах Школы прикладного бизнес-анализа в нашем лицензированном учебном центре обучения и повышения квалификации системных и бизнес-аналитиков в Москве:
- Основы архитектуры и интеграции информационных систем
- Разработка ТЗ на информационную систему по ГОСТ и SRS