Этот коммит содержится в:
ihar_hancharenka 2025-09-04 12:42:33 +03:00
родитель 3c47fbb5fa
Коммит 8e3a218ddd
4 изменённых файлов: 107 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,82 @@
******************************************************************************************************************************************************
Антипаттерн: N+1 запросов — как заметить и починить
Вы берёте список сущностей, а потом в цикле для каждой тянете связанные данные. В итоге - 1 запрос за «родителями» + N запросов за «детьми». Латентность растёт линейно от размера выборки.
Симптомы
- В логах много одинаковых коротких запросов.
- Кол-во запросов ≈ размеру списка.
- Страница/endpoint сильно «замедляется» при росте данных.
Плохой пример (SQL + псевдокод)
-- Берём пользователей
SELECT id, name FROM users WHERE active = true;
-- Потом в цикле по каждому:
SELECT count(*) FROM orders WHERE user_id = :id;
Правильно (SQL, PostgreSQL) — сетевое мышление:
SELECT u.id,
u.name,
count(o.*) AS orders_cnt
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.active = true
GROUP BY u.id, u.name;
Django ORM
# Плохо: в шаблоне/цикле обращаемся к user.orders -> N+1
users = User.objects.filter(active=True)
# Хорошо: подгрузим связи заранее
users = (User.objects
.filter(active=True)
.prefetch_related('orders')) # для 1:N
# для 1:1 / ForeignKey используйте select_related('profile')
# Агрегация без цикла
from django.db.models import Count
users = (User.objects.filter(active=True)
.annotate(orders_cnt=Count('orders')))
SQLAlchemy
from sqlalchemy.orm import selectinload, joinedload
# 1:N — безопаснее selectinload (батчирует IN (...))
users = (session.query(User)
.options(selectinload(User.orders))
.filter(User.active.is_(True))
.all())
# 1:1 — joinedload
user = (session.query(User)
.options(joinedload(User.profile))
.get(user_id))
Практические советы
- Логируйте кол-во запросов на эндпойнт/страницу. В Django - django-debug-toolbar, assertNumQueries в тестах; в SQLAlchemy - echo/интеграция с логгером.
- Индексы: обязательно orders(user_id); если фильтруете по статусу - составной (user_id, status).
- Батчинг вместо циклов: тяните детей одним запросом WHERE user_id IN (...), затем мапьте в памяти.
- Осторожно с joinedload для 1:N на больших выборках - риск «взрыва» строк. Для 1:N чаще выбирайте selectinload.
- Колонки по делу: не тащите SELECT *, берите только нужные поля.
- Пагинация: уменьшает N и давление на сеть/память.
- EXPLAIN (ANALYZE, BUFFERS) - проверяйте планы и кардинальности.
💡Думайте наборами, а не циклами. Eager loading + агрегаты закрывают 90% случаев N+1. Настройте мониторинг количества запросов - и ловите проблему до продакшена.
Сохрани, чтобы не наступить снова. Поделись с коллегами. А как вы ловите N+1 у себя?

Просмотреть файл

@ -8,6 +8,27 @@ environment:
volumes:
$PWD/data:/storage
docker run -it --rm --name windows \
-p 8006:8006 \
--device=/dev/kvm \
--device=/dev/net/tun \
--cap-add NET_ADMIN \
-v "${PWD}/windows:/storage" \
--stop-timeout 120 \
dockurr/windows
???
--stop-timeout
Timeout (in seconds) to stop a container, or -1 to disable timeout.
The --stop-timeout flag sets the number of seconds to wait for the container
to stop after sending the pre-defined (see --stop-signal) system call signal.
If the container does not exit after the timeout elapses, it is forcibly killed
with a SIGKILL signal.
If --stop-timeout is set to -1, no timeout is applied, and the daemon will
wait indefinitely for the container to exit.
2024
Wolfgang - Running Windows in a Docker Container! 0:00 of 10:06
https://www.youtube.com/watch?v=xhGYobuG508

Просмотреть файл

@ -0,0 +1,3 @@
2025
DanVega - Spring Boot Tutorial: Resolving Bean Ambiguity Like a Pro 0:00 of 16:51
https://www.youtube.com/watch?v=jCjHAD94c8A

1
science/ai/tools/client/cli-skynet.txt Обычный файл
Просмотреть файл

@ -0,0 +1 @@
https://github.com/hybridgroup/skynet