← Назад к вопросам
Как определить, что запрос к базе данных выполняется медленно?
2.0 Middle🔥 181 комментариев
#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Определение медленных запросов к БД
Это критический навык для любого backend разработчика. Медленные запросы — часто главная причина проблем с перформансом.
Метод 1: Логирование времени выполнения (самый простой)
import time
from contextlib import contextmanager
@contextmanager
def measure_query_time(query_name: str, slow_threshold: float = 0.1):
start = time.time()
try:
yield
finally:
duration = time.time() - start
if duration > slow_threshold:
print(f"SLOW QUERY: {query_name} took {duration:.3f}s")
else:
print(f"OK: {query_name} took {duration:.3f}s")
with measure_query_time("get_users", slow_threshold=0.05):
users = User.objects.all()
Метод 2: Django Debug Toolbar (для разработки)
# settings.py
INSTALLED_APPS = [
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
INTERNAL_IPS = ['127.0.0.1']
После этого в браузере видно все запросы, их время и SQL.
Метод 3: Анализ SQL запросов PostgreSQL
from django.db import connection
from django.test.utils import override_settings
@override_settings(DEBUG=True)
def analyze_queries():
from django.db import connection, reset_queries
from django.conf import settings
reset_queries()
settings.DEBUG = True
users = User.objects.filter(is_active=True)
for query in connection.queries:
print(f"Time: {query['time']}s")
print(f"SQL: {query['sql']}")
def slow_query_logger():
from django.db import connection
from django.conf import settings
if settings.DEBUG:
for query in connection.queries:
time = float(query['time'])
if time > 0.1:
print(f"SLOW: {query['sql']}")
print(f"Time: {time}s")
Метод 4: PostgreSQL EXPLAIN (самый точный)
def analyze_query_plan(sql: str):
import psycopg2
conn = psycopg2.connect(
dbname='mydb',
user='user',
password='password',
host='localhost'
)
cursor = conn.cursor()
cursor.execute(f"EXPLAIN ANALYZE {sql}")
plan = cursor.fetchall()
for line in plan:
print(line[0])
cursor.close()
conn.close()
Метод 5: Django ORM и проблемные паттерны
Проблема 1: N+1 query
# Плохо
users = User.objects.all()
for user in users:
print(user.profile.bio) # Для каждого user запрос!
# Хорошо
users = User.objects.select_related('profile').all()
for user in users:
print(user.profile.bio) # Уже загружено
# Для Many-to-Many
users = User.objects.prefetch_related('groups').all()
for user in users:
print(list(user.groups.all())) # Уже загружено
Проблема 2: Фильтрация после загрузки
# Плохо
active_users = [u for u in User.objects.all() if u.is_active]
# Хорошо
active_users = User.objects.filter(is_active=True)
Проблема 3: COUNT на большой таблице
# Плохо
count = User.objects.count() # Полное сканирование
# Хорошо
count = User.objects.filter(is_active=True).count() # С индексом быстро
# Очень большие таблицы
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE relname = 'auth_user'
""")
print(cursor.fetchone()[0])
Метод 6: Мониторинг в production с логированием
import logging
import time
from functools import wraps
logger = logging.getLogger('queries')
def log_slow_queries(threshold: float = 0.1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
duration = time.time() - start
if duration > threshold:
logger.warning(
f"Slow query in {func.__name__}: {duration:.3f}s",
extra={
'function': func.__name__,
'duration': duration,
}
)
else:
logger.debug(f"{func.__name__}: {duration:.3f}s")
return result
return wrapper
return decorator
@log_slow_queries(threshold=0.05)
def get_user_with_orders(user_id: int):
return User.objects.select_related('profile').prefetch_related(
'orders'
).get(id=user_id)
Метод 7: PostgreSQL встроенные инструменты
-- Найти самые медленные запросы
SELECT
query,
calls,
total_time,
mean_time,
max_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- Найти запросы с Sequential Scan
EXPLAIN ANALYZE SELECT * FROM users WHERE name LIKE '%john%';
-- Создать индекс
CREATE INDEX idx_users_name ON users(name);
Практический пример: Django с мониторингом
import logging
from functools import wraps
import time
logger = logging.getLogger('performance')
def track_query_performance(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
from django.db import connection, reset_queries
from django.conf import settings
reset_queries()
settings.DEBUG = True
start = time.time()
response = view_func(request, *args, **kwargs)
duration = time.time() - start
total_queries = len(connection.queries)
total_time = sum(float(q['time']) for q in connection.queries)
logger.info(
f"{view_func.__name__}: {total_queries} queries in {total_time:.3f}s (view: {duration:.3f}s)"
)
for query in connection.queries:
query_time = float(query['time'])
if query_time > 0.1:
logger.warning(f"Slow: {query['sql'][:100]} ({query_time:.3f}s)")
return response
return wrapper
@track_query_performance
def user_orders_view(request, user_id):
# Правильно — prefetch
user = User.objects.prefetch_related('orders').get(id=user_id)
orders = user.orders.all()
return JsonResponse({
'user': user.username,
'orders_count': len(orders),
})
Инструменты для мониторинга
- New Relic: APM мониторинг
- Datadog: анализ медленных запросов
- DjangoQueryCount: подсчёт запросов
- django-silk: профилирование в development
- pgAdmin: анализ PostgreSQL
Чеклист оптимизации
- Используй select_related() для ForeignKey
- Используй prefetch_related() для Many-to-Many
- Филтруй в БД, не в памяти
- Создай индексы для часто используемых полей
- Избегай COUNT на огромных таблицах
- Логируй медленные запросы
- Регулярно анализируй EXPLAIN ANALYZE
- Кешируй результаты где возможно