← Назад к вопросам
Какая масштабируемость у монолитной архитектуры?
3.0 Senior🔥 111 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Масштабируемость монолитной архитектуры
Монолитная архитектура имеет серьёзные ограничения на масштабируемость. Понимание этих ограничений критично при выборе между монолитом и микросервисами.
Что такое монолитная архитектура
Монолит — это одно приложение, где весь код (UI, business logic, data access) скомпилирован в один артефакт и развертывается как единое целое:
┌──────────────────────────────────────┐
│ Monolithic Application │
│ │
│ ┌──────────────────────────────┐ │
│ │ Presentation Layer (MVC) │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Business Logic Layer │ │
│ │ (Services, Rules) │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Data Access Layer (DAO) │ │
│ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
↓ ↓ ↓
Instance 1 Instance 2 Instance 3
(Copy) (Copy) (Copy)
Ограничения масштабируемости
1. Горизонтальное масштабирование (Horizontal Scaling)
Размножение копий приложения — только способ масштабирования монолита:
// Монолитное приложение: User Service + Order Service + Product Service вместе
@SpringBootApplication
public class ECommerceApplication {
@RestController
@RequestMapping("/users")
public class UserController {
// User endpoints
}
@RestController
@RequestMapping("/orders")
public class OrderController {
// Order endpoints
}
@RestController
@RequestMapping("/products")
public class ProductController {
// Product endpoints
}
}
// При масштабировании нужно запустить несколько копий ВСЕГО приложения
// Instance 1: User + Order + Product
// Instance 2: User + Order + Product
// Instance 3: User + Order + Product
Проблемы:
- Если трафик на
/usersвырос, нужно масштабировать ВЕСЬ монолит - Если
/productsможет обрабатывать нагрузку, все равно создаём копию всего приложения - Неэффективное использование ресурсов
2. Вертикальное масштабирование (Vertical Scaling)
Увеличение мощности одного сервера (больше CPU, RAM):
// Монолит ограничен мощностью одного сервера
public class MonolithPerformance {
// Single Instance монолита может иметь:
// - Одна JVM
// - Одно приложение
// - Один Tomcat (или другой контейнер)
// - Максимум ~64 GB RAM, ~96 CPU cores
// Но есть пределы:
// - GC паузы при 100+ GB памяти
// - Сложность управления одной большой JVM
}
Ограничения:
- Серверы имеют физические пределы
- Стоимость экспоненциально растёт
- GC паузы становятся проблемой с большой памятью
- Всё ещё один point of failure
3. Масштабируемость по функциональности
Монолит неделим, поэтому масштабировать по компонентам невозможно:
Монолит: структура жёсткая
┌──────────────────────────────────┐
│ User Service (10% нагрузки) │
│ Order Service (50% нагрузки) │ — нужно масштабировать
│ Product Service (40% нагрузки) │ всё целиком!
└──────────────────────────────────┘
Микросервисы: структура гибкая
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ User: 1x │ │ Order: 5x │ │ Product: 4x │
│ (10%) │ │ (50%) │ │ (40%) │
└──────────────┘ └──────────────┘ └──────────────┘
4. Масштабируемость по базе данных
Все компоненты используют одну БД:
@Service
public class MonolithDatabaseAccess {
private final JdbcTemplate jdbcTemplate;
// User Service запрашивает БД
public User getUser(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(), id
);
}
// Order Service запрашивает БД
public List<Order> getUserOrders(Long userId) {
return jdbcTemplate.query(
"SELECT * FROM orders WHERE user_id = ?",
new OrderRowMapper(), userId
);
}
// Product Service запрашивает БД
public List<Product> getProducts() {
return jdbcTemplate.query(
"SELECT * FROM products",
new ProductRowMapper()
);
}
}
// Проблемы:
// 1. Одна БД = один bottleneck
// 2. Сложно шардировать данные
// 3. JOIN операции на большие объёмы данных
// 4. Сложная миграция БД (ВСЕМ компонентам нужна новая схема)
Метрики масштабируемости монолита
Практические показатели для типичного Java монолита:
┌──────────────────────────────┬────────────────┐
│ Метрика │ Монолит │
├──────────────────────────────┼────────────────┤
│ Max instances (горизонтально)│ 5-10 │
│ Max RAM (вертикально) │ 64-128 GB │
│ Типичный размер артефакта │ 300 MB - 1 GB │
│ Время старта │ 2-5 минут │
│ Время деплоя │ 10-30 минут │
│ Максимум RPS на инстанцию │ 10k-50k │
│ Максимум одновременных user. │ 100k-500k │
└──────────────────────────────┴────────────────┘
Реальный пример: масштабирование монолита
// Монолит на 100k пользователей
public class MonolithicEcommerce {
// Start: 1 инстанция на одном сервере
// 100k пользователей × 2 запроса/мин = 3.3k RPS
// 1 инстанция может обрабатывать ~10k RPS
// Ладно! Но нужно 50% запас
// При 200k пользователей:
// 200k × 2 = 6.6k RPS
// Нужно 2 инстанции монолита
// Каждая инстанция = полная копия 1 GB кода
// Deploy time: 30 минут на обновление
// Проблемы:
// - User Service может обрабатывать нагрузку
// - Order Service требует масштабирования
// - Product Service совсем не нужно масштабировать
// Но нужно запустить 2 полные копии приложения!
// При 1M пользователей:
// 1M × 2 = 33k RPS
// Нужно 4-5 инстанций
// Проблемы:
// - Памяти требуется 4-5 GB (1GB × 5)
// - Дублирование кэшей
// - Сессии нужно синхронизировать (Redis)
// - GC паузы на каждой инстанции
// - Если одна инстанция упадёт, потеря 20% трафика
}
Проблемы высокой доступности (High Availability)
// Монолит требует careful failover strategy
public class MonolithHighAvailability {
// Если одна инстанция из 3 упадёт:
// 1. Load Balancer переадресует трафик
// 2. Оставшиеся 2 инстанции обрабатывают 150% нагрузки
// 3. GC паузы могут вырасти
// 4. Нужно дополнительное место (headroom) для failover
// 5. Требуется 4+ инстанц для безопасности (50% overhead)
// Кроме того:
// - Session state нужно хранить в Redis/Memcached (дополнительная сложность)
// - Cache coherency между инстанциями
// - Длительный deploy = риск downtime
}
Сравнение: Монолит vs Микросервисы
Монолит:
┌────────────────────────────────────┐
│ Маленькие приложения (100k users) │ ✓ OK
│ Команда 1-10 человек │ ✓ Просто
│ Требования простые и стабильные │ ✓ Хорошо
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ Большие приложения (1M+ users) │ ✗ Проблемы
│ Команда 50+ человек │ ✗ Конфликты
│ Высокие требования к нагрузке │ ✗ Ограничения
│ Частые деплои разных компонентов │ ✗ Риск
└────────────────────────────────────┘
Микросервисы:
┌────────────────────────────────────┐
│ Огромные приложения (10M+ users) │ ✓ Масштабируется
│ Команда 100+ человек │ ✓ Параллельная работа
│ Разнородные требования │ ✓ Разные технологии
│ Независимые деплои │ ✓ Быстрые обновления
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ Сложность операций │ ✗ Высокая
│ Требуется хорошая инфраструктура │ ✗ Docker/K8s
│ Сложность debugging │ ✗ Распределённые трассы
└────────────────────────────────────┘
Практические ограничения
Типичный монолит начинает испытывать проблемы при:
1. Код растёт > 500k строк
- Сложность управления
- Конфликты в Git
- Длительное компилирование
2. Команда > 30 человек
- Конфликты в коде
- Сложность координации
- Люди мешают друг другу
3. Трафик > 50k RPS на инстанцию
- Нужна полная копия приложения
- Неэффективное использование ресурсов
- GC паузы и проблемы с памятью
4. Требуется обновлять разные компоненты часто
- Полный deploy каждый раз
- Всё или ничего
- Высокий риск
Оптимизация монолита (до перехода на микросервисы)
// Кэширование
@Configuration
public class CachingConfig {
@Bean
public CacheManager cacheManager() {
return new RedisCacheManager(redisConnectionFactory());
}
}
// Асинхронная обработка
@Service
public class AsyncProcessing {
@Async
public void sendNotification(User user) {
// В отдельном потоке, не блокирует HTTP response
}
}
// Read replicas для БД
@Configuration
public class DatabaseConfig {
@Bean
@ReadOnly
public DataSource readonlyDataSource() {
return createReadReplica();
}
}
// Connection pooling
@Bean
public HikariDataSource dataSource() {
return HikariDataSource.builder()
.maximumPoolSize(20)
.minimumIdle(5)
.build();
}
Вывод
- Монолит хорошо для: маленьких и средних приложений (до 100k пользователей)
- Масштабируемость: в основном горизонтальная (копирование инстанций)
- Ограничения: 5-10 инстанций максимум, неэффективное использование ресурсов
- Проблемы: при 1M+ пользователях начинают проявляться серьёзные ограничения
- Решение: микросервисы для больших систем, но с большей сложностью операций
- Best Practice: монолит для MVP, переход на микросервисы при необходимости