N+1: найти и исправить
Вопрос
Расскажите, что такое проблема N+1 в Hibernate, как вы её находите и как исправляете.
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Определение: 1 запрос на список и N на связи
Причины: ленивые коллекции в цикле, EAGER ManyToOne, сериализация сущностей
Поиск: логи SQL, статистика Hibernate, тест на число запросов, APM
join fetch и EntityGraph
BatchSize или default_batch_fetch_size
Ограничения fetch: пагинация, MultipleBagFetchException; DTO-проекции
Что могут спросить следом
- Почему EAGER не решает проблему?
- Как совместить fetch коллекций и пагинацию?
Разбор
N+1 это когда загрузили список одним запросом, а потом для каждого элемента пошёл отдельный запрос за связью. Чаще всего причина в обходе ленивых коллекций в цикле, в EAGER @ManyToOne при JPQL-запросах и в сериализации сущностей в JSON при включённом OSIV. Нахожу по логам SQL в тестах, через hibernate.generate_statistics, в проде по APM, где видно сотни одинаковых запросов на один HTTP-вызов. Исправляю загрузкой нужного заранее: join fetch или @EntityGraph на методе репозитория. Если коллекций несколько или нужна пагинация, fetch не подходит: тогда сначала беру страницу id, потом догружаю связи одним запросом по IN, или включаю default_batch_fetch_size. Когда нужны только поля, беру DTO-проекцию. EAGER не лечит N+1, он лишь делает его постоянным.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.