N+1 проблема

Базовый Java
≈ 1 мин чтения

N+1 — один запрос превращается в серию дополнительных

Проблема N+1 возникает, когда приложение сначала получает список из N объектов одним запросом, а затем для каждого объекта отдельно загружает связанные данные. В результате вместо одного-двух осознанных запросов база получает 1 + N обращений.

List<Order> orders = entityManager.createQuery(
        "select o from Order o",
        Order.class)
    .getResultList();

for (Order order : orders) {
    System.out.println(order.getUser().getName());
}

Если связь user подгружается отдельно для каждого заказа, цикл может незаметно создать множество SQL-запросов. На небольших тестовых данных код выглядит нормально, а под реальной нагрузкой задержка резко растёт.

Лечить нужно конкретный сценарий загрузки

List<Order> orders = entityManager.createQuery(
        "select o from Order o join fetch o.user",
        Order.class)
    .getResultList();

join fetch явно просит получить нужную связь вместе с основной выборкой. Это один из способов устранить N+1 для конкретного запроса; в других случаях могут подойти entity graph, batch fetching или другая форма выборки.

Не лечи N+1 глобальным EAGER. Постоянная загрузка всех связей может заменить одну проблему другой. Сначала выясни, какие данные нужны конкретному use case и какие SQL-запросы реально выполняются.