Ответ
Основные проблемы были связаны с масштабированием и производительностью высоконагруженного сервиса:
-
Бутылочное горлышко в базе данных (PostgreSQL):
- Проблема: Высокий RPS (Requests Per Second) приводил к блокировкам (locks) при конкурентных
UPSERT-операциях. - Решение: Внедрили шардинг по ключу клиента, добавили частичные индексы и пересмотрели уровень изоляции транзакций.
- Проблема: Высокий RPS (Requests Per Second) приводил к блокировкам (locks) при конкурентных
-
Проблемы с кэшированием (Redis):
- Проблема: Один инстанс Redis не справлялся с нагрузкой, возникали таймауты.
- Решение: Перешли на Redis Cluster и добавили двухуровневое кэширование:
Redis(распределенный) +Caffeine(in-memory, локальный для каждого экземпляра приложения).
-
Проблема N+1 в ORM (Hibernate):
- Проблема: Некоторые API-эндпоинты генерировали десятки запросов к БД из-за ленивой загрузки связанных сущностей.
- Решение: Использовали
JOIN FETCHв JPQL и@EntityGraphдля eager-загрузки только необходимых данных за один запрос.
Пример оптимизации запроса N+1:
// БЫЛО: При получении списка пользователей для каждого отдельно выполнялся запрос за его заказами.
@Query("SELECT u FROM User u WHERE u.department = :dept")
List<User> findUsersByDepartment(String dept); // N+1 запрос!
// СТАЛО: Все данные загружаются за один запрос.
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.orders WHERE u.department = :dept")
List<User> findUsersByDepartmentWithOrders(String dept); // 1 запрос!
- Долгие сборки в CI/CD: Решили переходом на Gradle с использованием конфигурационного кэша и репозитория для зависимостей (Nexus).