Базы Данных (Data Base) | SQL & NoSQL. PostgreSQL, MySQL и Big Data. Уроки для Backend и Data Engineer. Архитектура БД, Оптимизация и Design.
IT и технологии · 16 июня 2026 г.
Антипаттерн: N+1 запросов и как его избежать
Антипаттерн: N+1 запросов и как его избежать Что такое N+1? При выборке связанных данных ORM (или вручную) сначала делается 1 запрос за основными записями, а потом N дополнительных - по одной для каждой записи, чтобы получить связанные объекты. Например, получить 10 пользователей и для каждого — список их заказов ⇒ 1 запрос к users + 10 запросов к orders. 🚩 # SQLAlchemy-пример “N+1”: users = session.query(User).all() # 1 запрос for u in users: print(u.orders) # для каждого пользователя — отдельный запрос Почему плохо? 🔹 Высокая нагрузка на базу: запросы “в тоненькую” вместо одного “тяжелого”. 🔹 Задержки сети: множество раунд-трипов увеличивает время ответа. 🔹 Масштабируемость страдает: при росте N время растёт линейно. Как победить N+1 1. Eager loading (предварительная загрузка) Загрузка связей сразу вместе с основными объектами. # SQLAlchemy, joinedload - делает JOIN и подтягивает данные сразу from sqlalchemy.orm import joinedload users = session.query(User).options(joinedload(User.orders)).all() for u in users: print(u.orders) # не генерирует дополнительных запросов ✅ Сокращает число запросов до 1. 2. Batch loading (групповые запросы) Если JOIN приводит к дублированию полей, можно сделать два запроса: -- 1: получить user_id SELECT id FROM users WHERE active = true; -- 2: получить все заказы для этих пользователей SELECT * FROM orders WHERE user_id IN (...список id...); ✅ Баланс между сложностью и производительностью. 3. DataLoader / кеширование В GraphQL и приложениях на Node.js часто используют DataLoader: 🔹 Собирает все ключи за тиковый цикл 🔹 Делает один общий запрос 🔹 Раздаёт результаты обратно 4. Правильное проектирование API • Предусматривайте, какие связи нужны на фронтенде, и загружайте их сразу. • Разделяйте endpoints: если нужны только пользователи без заказов - делайте лёгкий запрос. Best practices & подводные камни 🔹 EXPLAIN ANALYZE для проверки плана: убедитесь, что JOIN-ы и IN (…) не приводят к полному сканированию таблиц. 🔹 Пагинация: всегда ограничивайте выборку через LIMIT/OFFSET или курсоры. 🔹 Будьте осторожны с joinedload на “много ко многим” - может раздувать размер результата. Сохрани этот пост, чтобы не забыть, и поделись с коллегами! А у тебя были случаи, когда N+1 съедал всю производительность? Как борешься? #db 👉 @database_info