Mail.ru - сервис, предоставляющий пользователям возможность обмена электронными письмами.
Функционал:
- Просмотр писем в папках
- Сохранение черновика
- Отправка письма (в том числе в ответ и пересылкой)
- Удаление писем и черновиков
- Создание папки (добавление писем в папку)
- Фильтрация писем
- 50M MAU 1
- 16,2 DAU 2
- 20 000 000 писем отправляются в день 3
- Средняя продолжительность посещения - 7 мин 30 сек 4
- Почта mail.ru заблокировала более 6 млрд спам-писем за квартал 5
- 37% респондентов проверяют почту несколько раз в сутки, у 22% она всегда открыта в течение дня. Каждый пятый (20%) открывает сервисы раз в день, 11% – раз в неделю. 7% пользуются электронными письмам крайне редко, а 3% уже не помнят, когда проверяли папку «Входящие» последний раз. 6
- 50 пб - хранилище mail.ru. Из них 15 пб - письма, 35 пб - вложения (файлы) 7
- 12 млрд файлов в хранилищах 7
- 1 млн писем в минуту 8
- 8 гб - бесплатный размер хранилища писем mail.ru 8
- Средний вес письма без вложения 75 кб 9
- Максимальный размер вложений 25 мб
- 100 млн активных пользователей 10
- По данным специалистов Почты Mail.Ru около 50% всех писем в Мобильной Почте Mail.Ru отправляются с вложениями. В каждом пятом письме — два и более прикрепленных файла. Примерно в 80% случаях — это фотографии, 19% — документы, 1% приходится на видеоролики и архивы. 11
| Название | Показатель | Расчет |
|---|---|---|
| MAU | 50 млн/месяц | из данных |
| DAU | 17 млн/день | из данных |
| Отправка писем | 20 млн/день | из данных |
| Получение писем | 500 тыс/мин | из данных (в данных - пиковая нагрузка с коэффициентом 2) |
| Письмо без вложения | 75 кб | из данных |
| Письмо с вложением | 1125 кб | Из данных: фотографии: 80% (2000 кб), документы: 19% (500 кб), видеоролики и архивы: 1% (10000 кб) Средний вес вложения = (0.8×2000) + (0.19×500) + (0.01×10000) = 1795 кб 80% писем с вложениями содержат один файл 1795 кб + 75 кб = 1870 кб 20% писем с вложениями содержат два и более файла (возьмем 2): 2×1795 кб + 75 кб = 3665 кб Средний вес с вложением = (0.8×1870) + (0.2×3665) = 2229 кб Средний вес = (0.5×75) + (0.5×2229) = 1152 кб |
| Хранилище | 0,5 гб/пользователь | В открытых источниках нет данных о среднем хранилище пользователей и о количестве пользователей. Предположим, что 50 млн MAU - количество хранилищ, а ящики неактивных пользователей удаляются. 50 пб / 100 млн пользователей = 0,52 гб |
| Проверка почты | 3.5 раз/день | 37% — проверяют почту несколько раз в сутки (3 раза в день), 22% — почта всегда открыта в течение дня (10 проверок в день), 20% — проверяют почту раз в день, 11% — проверяют почту раз в неделю (0.14 проверки в день), 7% — пользуются почтой крайне редко (предположим, это 0.01 проверки в день), 3% — не помнят, когда проверяли почту (0 проверок в день). Среднее количество проверок в день = (0.37×3) + (0.22×10) + (0.20×1) + (0.11×0.14) + (0.07×0.01) + (0.03×0) = 3.5261 |
| Пользователи | 100 млн | В источнике указаны активные пользователи, однако если принять, что неактивные ящики удаляются (полгода бездействия) - можно взять это значение как количество пользователей |
| Запрос | RPS | Расчет RPS | Traffic | Расчет трафика |
|---|---|---|---|---|
| Создание и отправка письма | 231 | Отправка писем / 86400 = 20 млн / 86400 | 2034 мбит/с | Отправка писем * Письмо / 86400 = 20 млн * 1125 кб / 86400 |
| Просмотр страницы писем | 689 | Проверка почты * DAU / 86400 = 3.5 * 17 млн / 86400 | 20175 мбит/с | Предположим, что при проверке почты открываются входящие на 1 странице (50 писем). Для отображения входящих не требуются вложения, поэтому вес одного письма базовый без вложений Проверка почты * DAU * 50 * Письмо без вложения / 86400 = 3.5 * 17 млн * 50 * 75 / 86400 |
| Просмотр письма с вложениями | 2065 | Предположим, что при проверке почты пользователь открывает 3 письма с вложениями, скачивая их. 3 * Проверка почты * DAU / 86400 = 3 * 3.5 * 17 млн / 86400 |
18157 мбит/с | Скачивание 3 писем 3 * Проверка почты * DAU * Письмо с вложением / 86400 = 3 * 3.5 * 17 млн * 1125 кб / 86400 |
| Сохранение черновика | 4166 | Пусть черновик сохраняется автоматически каждые 10 секунд, а письмо в среднем пишется 3 минуты Отправка писем * (3 мин / 10 сек) / 86400 = 20 млн * (3 мин / 10 сек) / 86400 |
123 мбит/с | Пусть при написании черновика сохраняется изменение - 75 кб / (3 мин / 10 сек) = 4 кб. Сохранение вложения из черновика примем уже учтенным при расчете трафика при отправке Отправка писем * ( 3 мин / 10 сек) * 4 кб / 86400 = 20 млн * ( 3 мин / 10 сек ) * 4 кб / 86400 |
| Модерация спама | 8133 | Из данных о работе антиспам сервиса почты mail.ru, RPS на модерацию спама = RPS получения письма Получение писем / 60 = 1 млн / 60 |
4882 мбит/с | Так как антиспам-системы сканируют текстовое содержание, вес письма возьмем за 75 кб (письмо без вложения) Получение писем * Письмо без вложения / 60 = 500 тыс * 75 кб / 60 |
| Тип | Объем | Расчет |
|---|---|---|
| Письма | 15 пб | Из данных |
| Вложения | 35 пб | Из данных |
| Личная информация | 96 тб | Личная информация - данные (ФИО, пароль, техническая информация, почта) 10 кб + аватарка 1 мб = 1034 кб Пользователи * 1034 кб = 100 млн * 1034 кб |
Распределение трафика по странам 4
| Процент | Страна |
|---|---|
| 90 % | Россия |
| 4 % | Казахстан |
| 4 % | Белоруссия |
| 1 % | Азербайджан |
| 1 % | Германия |
Исходя из крупных городов этих стран и распределения нагрузки целесообразно ДЦ разместить в Москве
Будем использовать L4 и L7 балансировщики:
- Балансировка SMTP-трафика (L4):
NGINX
Алгоритм: Least Connections
Задачи: равномерное распределение нагрузки между SMTP-серверами. Используется L4 балансировка, так как анализ содержимого при отправке не происходит - Балансировка IMAP/POP3-трафика (L4):
NGINX
Алгоритм: Source IP Hash
Задачи: сохранение сессий пользователей, так как сессии IMAP/POP3 длительные - Балансировка HTTP/HTTPS-трафика (L7):
NGINX
Алгоритм: Round Robin для статики и Least Connections для API
Задачи: разделение трафика на статический контент и API - Оркестрация сервисами с помощью Kubernetes
- Обеспечение отказоустойчивости - keepalived
- SSL Termination - сертификаты SSL будет проверять балансировщик, для того, чтобы снизить нагрузку на сервера
| Поле | Тип данных | Размер (байты) |
|---|---|---|
| id | int | 4 |
| text | 255 | |
| username | text | 255 |
| password | text | 255 |
| avatar_url | text | 255 |
| created_at | timestamp | 8 |
| updated_at | timestamp | 8 |
| Итого | 1040 |
Размер - 100 млн пользователей * 1040 байт = 96 гб
Количество строк - 100 млн
| Поле | Тип данных | Размер (байты) |
|---|---|---|
| id | int | 4 |
| title | text | 255 |
| description | text | 255 |
| sender_email | text | 255 |
| recipient_email | text | 255 |
| message_id | int | 4 |
| parent_id | int | 4 |
| date | timestamp | 8 |
| isRead | bool | 1 |
| folder_id | int | 4 |
| created_at | timestamp | 8 |
| updated_at | timestamp | 8 |
| isDraft | bool | 1 |
| Итого | 1062 |
Размер - 100 млн пользователей * 100000 писем у каждого (среднее значение) * 1 кб = 10 пб. Расчет приблизительно верен, так как 15 пб - хранилище писем mail.ru без вложений
Количество строк - 10 000 000 000 000
| Поле | Тип данных | Размер (байты) |
|---|---|---|
| id | int | 4 |
| user_id | int | 4 |
| order_number | int | 4 |
| name | text | 255 |
| created_at | timestamp | 8 |
| updated_at | timestamp | 8 |
| is_system | bool | 1 |
| Итого | 292 |
Размер - 100 млн пользователей * (3 дефолтные папки (входящие, отправленные, спам) + 1 кастомная) * 292 байт = 81 гб
Количество строк - 400 000
| Поле | Тип данных | Размер (байты) |
|---|---|---|
| id_in_email | int | 4 |
| email_id | int | 4 |
| filename | text | 255 |
| contentType | text | 255 |
| file_id | int | 4 |
| Итого | 777 |
Размер - 12 000 000 000 * 777 байт = 3,4 пб
Количество строк - 12 000 000 000
| Поле | Тип данных | Размер (байты) |
|---|---|---|
| id | int | 4 |
| content | blob | 1050 * 1024 |
| created_at | text | 255 |
| Итого | 514 |
Размер - 12 000 000 000 файлов (^11) * 1309 кб (средний вес вложения) = 15 пб
Количество строк - 12 000 000 000
| Таблица | Запросы | RPS |
|---|---|---|
| User (чтение) | подгрузка профиля (в локал сторадж), вход | (DAU + DAU/2) / 86400 = 295 |
| User (запись) | регистрация, обновление профиля | MAU / 86400 = 578 |
| Email (чтение) | просмотр страницы писем | 689 |
| Email (запись) | отправка писем, сохранение черновика | 231 + 4166 = 4397 |
| Folder (чтение) | просмотр страницы писем | 689 |
| Attachment (чтение) | просмотр письма с вложением | 2065 |
| Attachment (запись) | отправка писем с вложениями | Отправка письма / 2 = 231/2 = 115 |
| File (чтение) | просмотр письма с вложением | 2065 |
| File (запись) | отправка писем с вложениями | (Отправка письма / 2) * 0.75 (предположим, что 25% писем с вложениями пересылаются и используется тот же файл) = (231/2)*0.75 = 87 |
USER
- Уникальный индекс для email для быстрой авторизации
- Индекс по date, folder_id, recipient_email дя быстрой загрузки папки с письмами
- Индекс для поиска по отправителю sender_email
- Индекс для сортировки писем по дате (recipient_email, created_at)
- Частичный индекс прочитанные/непрочитанные, черновик
ATTACHMENTS
- Индекс по email_id для быстрой загрузки
| Таблица | БД | Комментарий | Шардирование | Резервирование |
|---|---|---|---|---|
| Email, Attachments | Cassandra | Горизонтальное масштабирование (10 трлн записей) и высокая запись (4.3k RPS) | Шардирование по хэшу от почты пользователя | 3 копии в разных DC |
| Folder | PostgreSQL | Таблица редко изменяемая и небольшая, вне схем | Без шардирования | Синхронная репликация (1 мастер и 2 реплики) |
| User | PostgreSQL | Таблица редко изменяемая и небольшая, вне схем | Без шардирования | Синхронная репликация (1 мастер и 2 реплики) |
| File | Mail Storage S3 | Надежное, удобное хранение для файлов с быстрым доступом | По хэшу от почты + дате | Реплики, снапшоты |
- Поле unread_count в USER для хранения количества непрочитанных писем (избегаем COUNT при каждом входе)
- sender_username, recipient_username, folder_name в EMAIL (избегаем JOIN)
- has_attachments в EMAIL (избегаем JOIN)
- unread_count в FOLDER (избегаем COUNT)
- Redis для кэширования
- pgx библиотека для PostgreSQL для go
- aws/aws-sdk-go/service/s3 для S3 дя go
- Cassandra – gocql (Go)
| Технология | Область применения | Мотивация выбора |
|---|---|---|
| Go | Бэкенд-сервисы | Высокая производительность, многопоточность, микросервисная архитектура |
| React + TypeScript | Фронтенд | VDOM + строгая типизация |
| Cassandra | Основное хранилище писем | Горизонтальная масштабируемость, высокая доступность, оптимизирована для записи |
| Mail.ru Cloud Storage | Хранение файлов | Хранение вложений к письмам. Готовое S3-хранилище без затрат (написано разработчиками mail.ru) |
| PostgreSQL | Хранение пользователей и папок | Поддержка транзакций, надежность, хорошая производительность для структурированных данных |
| Redis | Кэширование | Для хранения сессий и горячих данных, сверхнизкие задержки |
| Rabbit MQ | Очереди сообщений | Обеспечение надежной доставки сообщений при высокой нагрузке |
| Kubernetes | Оркестрация сервисов | Автомасштабирование, управление жизненным циклом приложений |
| NGINX | Балансировка нагрузки | Высокая производительность, поддержка SSL терминации |
| Prometheus | Мониторинг | Сбор и хранение метрик в режиме реального времени |
| Grafana | Визуализация метрик | Анализ производительности |
| Технология | Способ резервирования |
|---|---|
| PostgreSQL (User, Folder) | Master-slave репликация инструментами PostgreSQL с помощью WAL. Синхронная репликация (1 мастер и 2 реплики). |
| Cassandra (Email, Attachments) | Репликация между 3 дата-центрами. Шардирование по хэшу от почты пользователя. Автоматическое восстановление при сбоях. |
| Mail.ru Cloud Storage | Репликация в 3 экземплярах в одном дата-центре, георепликация, snapshots |
| Redis | RDB для создания снимков данных с определённым интервалом. Репликация master-slave для отказоустойчивости. |
| Nginx | При отказе трафик перенаправляется на резервный балансировщик |
| Rabbit MQ | Репликация партиций, хранение логов, снепшоты, резервные брокеры |
Микросервисы:
- authservice
- mailservice
- fileservice
- userservice
- deliveryservice
Микросервисы
deliveryservice
RPS отправка письма 231
mailservice
RPS просмотр страницы писем 689 + отправка писем 231 + сохранение черновика 4166
userservice
RPS 295 (подгрузка профиля) + 578 (регистрация, обновление профиля)
authservice
RPS 295 (вход, регистрация)
fileservice
RPS отправка письма 115 + просмотр письма с вложением 2065
| Сервис | RPS | Пиковый RPS (×2) | CPU (ядер) | RAM (GB) | Обоснование |
|---|---|---|---|---|---|
| DeliveryService | 231 | 462 | 4 | 8 | Лёгкая нагрузка: 1 ядро = ~100 RPS (Go) |
| MailService | 5086 | 10 172 | 1020 | 102 | Средняя нагрузка: 1 ядро = ~100 RPS (Go) |
| UserService | 873 | 1 746 | 18 | 18 | Средняя нагрузка |
| AuthService | 295 | 590 | 6 | 6 | Лёгкая нагрузка |
| FileService | 2180 | 4 360 | 440 | 44 | Средняя нагрузка |
| Nginx L4 | 7500 | 15 000 | 30 | 30 | 1 ядро = 500 CPS |
| Nginx L7 | 7500 | 15 000 | 30 | 30 | 1 ядро = 500 RPS |
| Rabbit MQ | 230 | 460 | 2 | 16 | Лёгкая нагрузка |
| Redis | 295 | 590 | 2 | 16 | Лёгкая нагрузка |
| Prometheus | 7500 | 15 000 | 4 | 32 | Лёгкая нагрузка |
| Хранилища | RPS | CPU | Объем |
|---|---|---|---|
| Cassandra | 689 чтение + 231 запись + 4166 запись = 5086. С учетом репликаций и пиковых значений - 20000 | 2000 ядер | 30 пб |
| S3 mail storage | 115 запись + 2065 чтение. С учетом репликаций и пиковых значений - 10000 | 100 ядер | 45 пб |
| PostgreSQL | 295 + 578. С учетом репликаций и пиковых значений - 3500 | 35 ядер | 200 гб |
Выбрана модель развертывания с помощью оркестрации в kubernetes. Из-за размера, логичнее развертывать на собственном железе cassandra, а s3 хранение на облачном хранилище экосистемы VK
| Название | Хостинг | Конфигурация | Cores | Cnt | Покупка | Аренда |
|---|---|---|---|---|---|---|
| Storage Node | own | Xeon E5-2690/2x 64GB/24x30TB 2.5 NVMe | 1344 | 42 | 6.7M$ | - |
| k8s-cluster | vkcloud | 10x(1CPU/64GB/20HDD) + LB | 80 | 10 | - | 165,562₽/мес |
| nginx | own | AMD EPYC 7313P 32GB 250GB | 160 | 10 | 26280 $ | - |
https://docs.google.com/presentation/d/1ZHeDCVgibnvxEd7Yey8N097Jg1762YRYZGifeb69R4M/edit?usp=sharing