← Назад к вопросам

Какая масштабируемость у монолитной архитектуры?

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, переход на микросервисы при необходимости
Какая масштабируемость у монолитной архитектуры? | PrepBro