Ответ
Проблема N+1 возникает, когда для получения связанных данных выполняется один основной запрос, а затем N дополнительных запросов для каждой записи из основного результата. Это приводит к неоптимальной работе приложения.
Пример с React и GraphQL:
// Плохо: запрашиваем пользователей, затем отдельно их заказы
const { data: users } = useQuery(GET_USERS);
users.map(user => (
<div key={user.id}>
{user.name}
<Orders userId={user.id} /> // Внутри Orders свой запрос GET_ORDERS
</div>
));
Решение - загружать связанные данные одним запросом:
query GetUsersWithOrders {
users {
id
name
orders {
id
total
}
}
}
Проблема характерна для ORM, GraphQL, REST API. Оптимизировать можно через:
- Жадную загрузку (eager loading)
- Пакетные запросы
- Даталоадеры (GraphQL)
Ответ 18+ 🔞
А, слушай, вот эта ваша проблема N+1 — это же классика жанра, просто пиздец! Представь себе: ты такой красавчик, делаешь один запрос, получаешь, скажем, список пользователей. А потом, как идиот, для каждого пользователя по отдельности лезешь в базу за его заказами. Получается один главный запрос и потом ещё N (по количеству пользователей) дополнительных. Это ж овердохуища запросов, сервер просто ляжет и скорчится, как манда с ушами!
Вот смотри, как это выглядит в коде, если делать по-распиздяйски:
// Плохо: запрашиваем пользователей, затем отдельно их заказы
const { data: users } = useQuery(GET_USERS);
users.map(user => (
<div key={user.id}>
{user.name}
<Orders userId={user.id} /> // Внутри Orders свой запрос GET_ORDERS
</div>
));
Видишь? Получили юзеров, а потом для каждого юзера компонент Orders полезет делать свой собственный запрос. Это ж ебаный цирк, а не оптимизация! Сервер будет охуевать от такого напора.
А решение-то проще пареной репы, ёпта! Надо не распыляться, а взять и за один раз всё, что нужно, выгрести. Как шваброй по полу. В GraphQL это делается элементарно:
query GetUsersWithOrders {
users {
id
name
orders { // Всё! Заказы сразу прицепляются к пользователю в одном запросе.
id
total
}
}
}
Вот и вся магия. Один запрос — и у тебя уже есть пользователи вместе с их заказами. Никакой лишней возни.
Эта проблема, кстати, вездесущая, как хитрая жопа. Встречается и в ORM, и в REST API. Но и решений на неё — хоть залейся. Можно использовать жадную загрузку (eager loading), чтобы связанные данные подтягивались сразу. Можно делать пакетные запросы, чтобы вместо тысячи мелких — один здоровенный. В том же GraphQL есть даталоадеры, которые такие N+1 запросы в фоновом режиме собирают в пачки и оптимизируют.
Главное — не быть как тот Герасим из рассказа, который всё по одному делал. Думай головой, э бошка думай! Сделал один раз — и свободен.