Ползём в сеньоры. Вопросы к собеседованию на сеньора Python.
5 subscribers
4 links
Подготовка какие вопросы могут задавать для сеньора по python и framework
Download Telegram
🔥 ПРОБЛЕМА N+1 — ЭТО ТО, ЧТО РАЗРУШАЕТ ПРОИЗВОДИТЕЛЬНОСТЬ ТВОЕГО ПРИЛОЖЕНИЯ. И НА СОБЕСЕ НА SENIOR ОБЯЗАТЕЛЬНО СПРОСЯТ, КАК ЕЁ РЕШАТЬ В SQLALCHEMY.

Представь: один запрос получает 100 авторов, а потом для каждого автора выполняется отдельный запрос, чтобы получить его книги. Это 101 запрос вместо одного! 💥 Масштабируй до тысяч записей — и приложение просто ляжет.

ПОЧЕМУ ЭТО ПРОИСХОДИТ? ДЕФОЛТНЫЙ LAZY LOADING.

По умолчанию SQLAlchemy использует ленивую загрузку (lazy loading). Отношения (relationship) загружаются только в момент обращения к ним. Удобно для разработки, но катастрофа для продакшена.

СПАСЕНИЕ — ЖАДНАЯ ЗАГРУЗКА (EAGER LOADING).

Нужно явно указать ORM загрузить связанные данные СРАЗУ в основном запросе. Два главных инструмента:

1. joinedload() — загрузка через JOIN.
• Идеально для небольших one-to-many связей.
• Делает один запрос с LEFT JOIN.
session.query(Author).options(joinedload(Author.books)).all()

2. selectinload() — загрузка через отдельный запрос с IN.
• ЛУЧШИЙ ВЫБОР для many-to-many или нескольких отношений.
• Сначала загружает родительские объекты, потом одним запросом все дочерние по списку ID.
session.query(Author).options(selectinload(Author.books)).all()

🚨 ГЛАВНАЯ ЛОВУШКА ДЛЯ SENIOR: КАРТЕЗИАНОВО ПРОИЗВЕДЕНИЕ С МНОГИМИ JOINEDLOAD.

Если у автора есть книги и статьи, и ты сделаешь так:
options(joinedload(Author.books), joinedload(Author.articles))

Получишь JOIN трёх таблиц. Если у автора 10 книг и 5 статей, SQL вернёт 50 строк для одного автора! Это дикий оверхед.

ПРАВИЛЬНОЕ РЕШЕНИЕ: Использовать selectinload для нескольких отношений.
options(selectinload(Author.books), selectinload(Author.articles))

Всего 3 запроса (авторы, книги, статьи) и НИКАКИХ лишних данных.

ПРОДВИНУТЫЕ ТЕХНИКИ, КОТОРЫЕ ТЫ ДОЛЖЕН ЗНАТЬ:

load_only() — загружай только нужные колонки, экономь память и сеть.
selectinload(Author.books).load_only(Book.title, Book.year)

contains_eager() — используй, когда сам делаешь JOIN с фильтрацией и хочешь результат "привязать" к отношению.

Сессии (Session) — это твой Unit of Work. Контекстный менеджер — твой друг. Никогда не забывай про session.close() или используй scoped_session для веб-приложений. На собесе спросят про lifecycle объекта и dirty tracking.

АЛГОРИТМ ОПТИМИЗАЦИИ НА ПРОЕКТЕ:
1. Включи логгирование запросов: echo=True в движке.
2. Найди в логах паттерн N+1 (один запрос, а потом много похожих).
3. Замени ленивую загрузку на жадную с помощью options().
4. Выбирай selectinload по умолчанию для надёжности. joinedload — только для простых случаев.
5. Профилируй и сравнивай время выполнения ДО и ПОСЛЕ.

💡 ИНСАЙТ: Глобально менять lazy='joined' в модели — плохая практика. Это может неожиданно добавить JOINы в других частях кода. Всегда контролируй загрузку на уровне запроса через options().

Запомни: на уровне Senior важно не просто знать методы, а понимать, КОГДА и ПОЧЕМУ каждый из них работает лучше. Умение диагностировать и устранять N+1 — это базовый скилл для работы с высоконагруженными системами.

#SQLAlchemy #Python #Senior
🚀 **Проблема N+1 в SQLAlchemy: как joinedload спасает твою базу данных**

Ты Junior, но хочешь звучать как Senior на собеседовании? Тогда слушай сюда. Один из самых частых вопросов на тех. интервью — "Что такое проблема N+1 и как её решить в SQLAlchemy?". Если ответишь правильно — ты уже на шаг ближе к офферу. Давай разберёмся просто и без воды.

**Что за зверь N+1?**
Представь: ты пишешь приложение на FastAPI с SQLAlchemy. Есть таблица `User` и `Post` (один пользователь — много постов). Ты хочешь вывести всех пользователей и их посты. Если ты сделаешь так:

users = session.query(User).all()
for user in users:
print(user.posts)


То SQLAlchemy сначала выполнит 1 запрос: `SELECT * FROM users`. А потом для каждого пользователя (а их, скажем, 100) сделает ещё по одному запросу: `SELECT * FROM posts WHERE user_id = ?`. Итого: 1 + 100 = 101 запрос. Вот это и есть **N+1** — катастрофа для производительности. База данных плачет, сервер тормозит, а тимлид грустно смотрит на тебя.

**Как решить? Вжух — и joinedload!**
В SQLAlchemy есть магия — `joinedload`. Она делает один большой запрос с `JOIN`, загружая все данные сразу. Смотри:

from sqlalchemy.orm import joinedload

users = session.query(User).options(joinedload(User.posts)).all()
for user in users:
print(user.posts) # Никаких дополнительных запросов!


Теперь SQLAlchemy выполнит всего 1 запрос: `SELECT users.*, posts.* FROM users LEFT JOIN posts ON users.id = posts.user_id`. Все данные уже в памяти. Быстро, эффективно, красиво.

**Но есть нюансы (куда без них)**
- `joinedload` меняет структуру запроса — не используй его, если потом фильтруешь по загруженным данным. Для фильтрации бери `contains_eager`.
- Если связей много (например, пользователь -> посты -> комментарии), не злоупотребляй `joinedload` — один `JOIN` ещё ок, а три уже могут тормозить. Для глубоких связей лучше `selectinload`.
- `joinedload` по умолчанию делает `LEFT OUTER JOIN`. Если тебе нужен `INNER JOIN`, используй `joinedload(User.posts, innerjoin=True)`.

**Как это поможет на собеседовании?**
Когда тебя спросят про N+1, не просто скажи "это плохо". Расскажи:
1. Что такое N+1 (пример с пользователями и постами).
2. Как `joinedload` решает проблему (один запрос вместо сотни).
3. Когда его не стоит использовать (фильтрация, глубокая вложенность).

Senior отличается от Junior тем, что знает не только "как", но и "почему". Покажи глубину — и ты в топе.

#SQLAlchemy #Python #Senior