Профилирование Django: silk, debug-toolbar и APM под нагрузкой
Один эндпоинт стал отдавать ответ за 4 секунды вместо 200 мс. Разбираем, какой инструмент профилирования Django показывает причину — debug-toolbar в разработке, django-silk на стейдже, APM в проде — и чем каждый платит за детализацию.

Эндпоинт списка заказов, который раньше отвечал за 200 мс, начал отдавать ответ за 4 секунды. Нагрузка не выросла, кода никто не трогал — а p95 уполз в потолок. Три инструмента отвечают на вопрос «где время»: django-debug-toolbar смотрит один запрос в браузере, django-silk пишет историю запросов на стенде, APM (Elastic APM, Sentry, Datadog) видит распределённую картину в проде. Каждый показывает свой срез и стоит своих накладных расходов. Ниже — когда какой брать и где каждый ломается.
Статья про Django 5.x. Всё, что ниже, про поиск источника задержки: N+1, медленный SQL, лишние вызовы во вьюхе. Оптимизация самих запросов — отдельная тема, здесь только про то, как найти виновника.
Что показывает каждый инструмент
Разница не в том, «какой лучше», а в том, что именно инструмент видит и где живёт.
- debug-toolbar — панель поверх HTML-ответа. Видит SQL конкретного запроса, время шаблонов, кеш, сигналы. Работает только при
DEBUG = Trueи только для запросов, которые возвращают HTML. Для JSON-API бесполезен без обходных приёмов. - silk — middleware, который пишет каждый запрос в свою базу: URL, время, все SQL с трейсбеком места вызова. Работает и на API, и в фоне. Есть декоратор для профилирования произвольной функции. Хранит историю — можно вернуться и сравнить.
- APM — агент, отправляющий трейсы во внешний сервис. Видит распределённые вызовы: Django, обращения к БД, Redis, внешние HTTP. Держит sampling, чтобы не писать весь трафик. Единственный из трёх, который безопасно оставить в проде.
Цена детализации
debug-toolbar и silk дают полную картину одного запроса, но замедляют его в разы — silk пишет каждый SQL с трейсбеком, это заметный оверхед. APM платит противоположным: он быстрый, но при sampling 10% нужного медленного запроса может в трейсах не оказаться.
| Критерий | debug-toolbar | silk | APM |
|---|---|---|---|
| Где применять | локальная разработка | стейдж, редко прод | прод |
| Тип запросов | только HTML | HTML и API | любые + фон |
| SQL с трейсбеком | да | да | частично |
| Оверхед | высокий | высокий | низкий (с sampling) |
| Хранит историю | нет | да, в своей БД | да, внешний сервис |
| Распределённые вызовы | нет | нет | да |
debug-toolbar: быстрый взгляд на один запрос
Когда N+1 подозревается на конкретной странице и она отдаёт HTML — это самый быстрый способ. Панель SQL показывает число запросов и дубли красным.
# settings.py (Django 5.x, django-debug-toolbar 4.x)
if DEBUG:
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
# без этого списка панель не покажется даже на localhost
INTERNAL_IPS = ["127.0.0.1"]
# urls.py
from django.conf import settings
from django.urls import include, path
urlpatterns = [
# ваши маршруты
]
if settings.DEBUG:
urlpatterns += [path("__debug__/", include("debug_toolbar.urls"))]
Откройте страницу — справа появится панель. Вкладка SQL с числом запросов рядом с одинаковыми строчками означает N+1. Клик по запросу разворачивает трейсбек: видно строку во вьюхе или шаблоне, откуда ушёл SQL.
Если во вкладке SQL вы видите 200 почти одинаковыхSELECT ... WHERE id = ?— это не медленная база, это забытыйselect_related/prefetch_related. База отвечает быстро, убивает количество раундтрипов.
Где debug-toolbar не поможет
API, отдающий JSON, панель не покажет — она встраивается в HTML. Для DRF-эндпоинтов люди используют пакеты вроде панелей истории запросов, но проще перейти на silk. И toolbar категорически нельзя оставлять с DEBUG = True в проде — это дыра в безопасности и утечка данных.
silk: история запросов на стенде
Когда медленный эндпоинт — это API, или проблема плавает и её надо поймать в истории, ставьте silk. Он пишет каждый обработанный запрос в отдельные таблицы.
# settings.py (django-silk 5.x)
INSTALLED_APPS += ["silk"]
# middleware silk должен идти как можно раньше,
# но после middleware, которое ставит request.user, если он нужен в отчётах
MIDDLEWARE += ["silk.middleware.SilkyMiddleware"]
# ограничьте объём: profiler под нагрузкой заполнит БД мгновенно
SILKY_PYTHON_PROFILER = True # cProfile по каждому запросу
SILKY_MAX_RECORDED_REQUESTS = 10000 # старые записи вытесняются
# urls.py
urlpatterns += [path("silk/", include("silk.urls", namespace="silk"))]
После migrate откройте /silk/. Список запросов сортируется по времени ответа и по числу SQL-запросов — сразу видно, где N+1. По каждому запросу открывается список SQL с временем и трейсбеком.
Отдельная функция вне HTTP-запроса — фоновая задача, команда management — профилируется декоратором или контекстным менеджером:
from silk.profiling.profiler import silk_profile
@silk_profile(name="Пересчёт остатков")
def recalculate_stock(warehouse_id):
# тело задачи; в /silk/ появится отдельная запись
...
Где silk ломается
- База silk растёт быстрее основной. Под реальным трафиком без
SILKY_MAX_RECORDED_REQUESTSи без периодической очистки таблицы разрастаются за часы. Держите silk на стейдже, а в проде — только точечно и ненадолго. - Оверхед искажает замеры.
SILKY_PYTHON_PROFILER = Trueоборачивает каждый запрос в cProfile. Абсолютные времена в silk будут выше реальных — сравнивайте относительно, а не как SLA. - Пишет он сам через ORM. Запись метрик — это дополнительные INSERT в ту же или отдельную базу. Настройте
SILKY_ANALYZE_QUERIESи хранение осознанно, иначе профайлер начнёт мешать тому, что профилирует.
APM: единственный вариант для прода
Когда медленный запрос воспроизводится только под реальной нагрузкой и только у части пользователей, ни toolbar, ни silk не поймают его без риска положить сервис. Здесь работает APM — Elastic APM, Sentry Performance или Datadog. Агент инструментирует Django, драйвер БД и клиенты Redis/HTTP, собирает трейс и отправляет во внешний сервис с настраиваемым sampling.
Конкретный синтаксис подключения зависит от вендора и версии агента — сверяйте с документацией провайдера, а не подставляйте пример вслепую. Общая схема одинакова: агент добавляется в INSTALLED_APPS или подключается через middleware, в настройках указывается адрес коллектора и transaction_sample_rate.
Что APM даёт такого, чего нет у первых двух:
- Распределённый трейс. Видно, что из 4 секунд 3.2 ушло на внешний HTTP к платёжному шлюзу, а не в SQL — этого не покажет ни toolbar, ни silk, они не выходят за пределы Django.
- p50/p95/p99 по эндпоинту. Не «этот запрос медленный», а «этот эндпоинт медленный у 5% пользователей» — с распределением, а не средним.
- Sampling. Пишется, например, 10% транзакций — оверхед остаётся приемлемым для прода.
Где APM ломается
Sampling — это и достоинство, и ловушка. При частоте 10% редкий, но критичный медленный запрос может не попасть в трейсы, и вы будете думать, что проблемы нет. Для отладки конкретного инцидента временно поднимите sample rate или включите правило, которое пишет весь набор медленных транзакций. Второй подвох — APM показывает, что SQL занял 3 секунды, но редко объясняет почему на уровне плана запроса. Для этого понадобится EXPLAIN ANALYZE в самой БД — APM только указывает пальцем.
Когда что выбирать
- Проблема на HTML-странице, воспроизводится локально → debug-toolbar. Тридцать секунд на просмотр вкладки SQL.
- Медленный API или фоновая задача, воспроизводится на стенде → silk. Ловите в историю, смотрите трейсбек SQL.
- Плавает только в проде, у части пользователей, возможно из-за внешних сервисов → APM. Единственный, кого безопасно там держать постоянно.
Часто цепочка такая: APM показал медленный эндпоинт в проде → воспроизвели на стейдже под silk → нашли N+1 → чинят через prefetch_related и проверяют debug-toolbar локально.
Что делать дальше
Поставьте debug-toolbar в dev прямо сейчас — это нулевой риск и мгновенная польза на каждом код-ревью фронтовых вьюх. silk включайте на стейдже с обязательным SILKY_MAX_RECORDED_REQUESTS и заданием на очистку его таблиц. APM подключайте в прод с осознанным sample rate и правилом «100% для транзакций дольше N мс». После того как профайлер указал на конкретный SQL — переходите к EXPLAIN ANALYZE и разбору плана запроса на стороне базы: инструменты Django говорят «где», база отвечает «почему».
Субтитры и таймкоды: Расшифровка речи и субтитры по правилам читаемости: 42 знака в строке, две строки, минимум секунда на реплику.
Спросить за 40 ₽Источники: django-silk — репозиторий и документация, django-debug-toolbar — официальная документация
Частые вопросы
У меня Django 4.2, а не 5.x — примеры подойдут?
Можно ли оставить silk работать в проде постоянно?
debug-toolbar не показывается на localhost — что проверить?
APM показал 200 SQL-запросов, но толком не видно откуда — что делать?
Профайлер показывает время больше, чем реальный ответ пользователю — почему?
Как понять, что медленный SQL — это N+1, а не просто тяжёлый запрос?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.