зеркало из
				https://github.com/iharh/notes.git
				synced 2025-10-30 21:26:09 +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: | volumes: | ||||||
|   $PWD/data:/storage |   $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 | 2024 | ||||||
| Wolfgang - Running Windows in a Docker Container! 0:00 of 10:06 | Wolfgang - Running Windows in a Docker Container! 0:00 of 10:06 | ||||||
|     https://www.youtube.com/watch?v=xhGYobuG508 |     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
	 ihar_hancharenka
						ihar_hancharenka