зеркало из
https://github.com/iharh/notes.git
synced 2025-10-29 12:46:06 +02:00
m
Этот коммит содержится в:
родитель
3c47fbb5fa
Коммит
8e3a218ddd
82
db/sql/features/query/antipatterns/nplusone.txt
Обычный файл
82
db/sql/features/query/antipatterns/nplusone.txt
Обычный файл
@ -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
|
||||
|
||||
3
pl/java/libfws/spring/core/docs/presentations.txt
Обычный файл
3
pl/java/libfws/spring/core/docs/presentations.txt
Обычный файл
@ -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
Обычный файл
1
science/ai/tools/client/cli-skynet.txt
Обычный файл
@ -0,0 +1 @@
|
||||
https://github.com/hybridgroup/skynet
|
||||
Загрузка…
x
Ссылка в новой задаче
Block a user