Овладейте практическими методами Брендана Грегга (USE, RED, flame-графы) для исследования задержек и узких мест в продакшне на базе данных, а не догадок.

Брендан Грегг — один из самых влиятельных авторов по производительности систем, особенно в мире Linux. Он написал широко используемые книги, создал практичные инструменты и — что важнее — поделился ясными методами расследования реальных проблем в продакшне. Команды принимают его подход потому, что он работает в условиях давления: когда задержки растут и все хотят ответов, нужен способ перейти от «возможно, это X» к «это точно Y» с минимальными драмами.
Методология производительности — это не единый инструмент и не хитрая команда. Это повторяемый способ расследования: чеклист того, что смотреть в первую очередь, как интерпретировать увиденное и как решать, что делать дальше.
Эта повторяемость и уменьшает гадания. Вместо того чтобы полагаться на того, у кого больше интуиция (или громче мнение), вы следуете последовательному процессу, который:
Многие расследования задержек идут прахом в первые пять минут. Люди сразу же переходят к исправлениям: «добавьте CPU», «перезапустите сервис», «увеличьте кэш», «подкорректируйте GC», «должна быть сеть». Иногда такие действия помогают — чаще они скрывают сигнал, тратят время или создают новый риск.
Методы Грегга побуждают отложить «решения», пока вы не ответите на более простые вопросы: что насыщено? что даёт ошибки? что стало медленнее — пропускная способность, очередь или отдельные операции?
Это руководство поможет сузить масштаб, замерить правильные сигналы и подтвердить узкое место до оптимизации. Цель — структурированный рабочий процесс для расследования задержек и задач профилирования в продакшне, чтобы результаты не зависели от удачи.
Задержка — это симптом: пользователи ждут дольше завершения работы. Причина обычно где-то ещё — конкуренция за CPU, ожидания диска или сети, конкуренция за локи, сборки мусора, очередь или задержки в удалённых зависимостях. Измерение только задержки показывает наличие боли, но не место её происхождения.
Эти три сигнала связаны:
Перед тюнингом захватите все три сигнала для одного и того же временного окна. Иначе вы можете «починить» задержку, просто сбросив нагрузку или заставив систему быстрее падать.
Средняя задержка скрывает пики, которые запоминают пользователи. Сервис со средней 50 мс всё ещё может часто зависать на 2 с.
Отслеживайте персентили:
Также следите за формой распределения: стабильный p50 с раcтущим p99 часто указывает на прерывистые зависания (например, contention на локах, I/O-скачки, паузы stop-the-world), а не на общее замедление.
Бюджет задержки — простая модель учёта: «Если запрос должен уложиться в 300 мс, куда может тратиться это время?» Разбейте на корзины, например:
Этот бюджет задаёт первую задачу измерения: найдите, какая корзина выросла во время всплеска, затем исследуйте эту область вместо слепого тюнинга.
Работа с задержками идёт наперекосяк, когда «система медленная» — слишком расплывчатое описание. Методы Грегга начинают раньше: зафиксируйте проблему в конкретном тестируемом вопросе.
Запишите две фразы перед тем, как касаться инструментов:
Это предотвратит оптимизацию не того слоя — например, CPU-хоста — когда боль локализована в одном эндпойнте или зависимости.
Возьмите окно, соответствующее жалобе, и если возможно, включите период «хорошего» состояния для сравнения.
Явно ограничьте область исследования:
Точность на этом этапе ускорит дальнейшие шаги (USE, RED, профилирование), потому что вы будете понимать, какие данные должны измениться, если гипотеза верна.
Отметьте деплои, изменения конфигурации, сдвиги трафика и события инфраструктуры — но не приписывайте им причинно-следственную связь. Записывайте их как «Если X, то мы ожидали бы Y», чтобы быстро подтверждать или отвергать.
Небольшой лог предотвращает дублирование работы между коллегами и упрощает передачу дела.
Time | Question | Scope | Data checked | Result | Next step
Даже пять строк могут превратить стрессовый инцидент в повторяемый процесс.
Метод USE (Utilization, Saturation, Errors) — быстрый чеклист Грегга для сканирования «большой четвёрки» ресурсов — CPU, память, диск (storage) и сеть — чтобы перестать гадать и начать сужать проблему.
Вместо того чтобы рассматривать десятки дашбордов, задавайте три одинаковых вопроса для каждого ресурса:
При последовательном применении это становится быстрым обзором, где существует «напряжение».
Для CPU utilization — % занятости CPU, saturation проявляется как run-queue или потоки, ожидающие выполнения, а ошибки могут быть связаны с троттлингом (в контейнерах) или проблемными прерываниями.
Для памяти utilization — используемая память, saturation часто выглядит как свап/пайджинг или частые сборки мусора, а ошибки — ошибки выделения или OOM-события.
Для диска utilization — % занятости устройства, saturation — глубина очереди и время ожидания операций чтения/записи, а ошибки — I/O-ошибки или таймауты.
Для сети utilization — пропускная способность, saturation — дропы/очереди/задержки, а ошибки — ретрансляции, сбросы соединений или потеря пакетов.
При жалобах пользователей сигналы saturation часто оказываются наиболее информативными: очереди, время ожидания и contention обычно коррелируют с задержкой сильнее, чем сырая загрузка.
Метрики уровня сервиса (латентность запросов, уровень ошибок) показывают влияние. USE показывает, куда смотреть дальше, выявляя ресурс под нагрузкой.
Практическая петля:
Метод RED держит вас ориентированными на пользовательский опыт до углубления в графы хостов.
RED не даёт вам гоняться за «интересными» системными метриками, которые не влияют на пользователей. Он заставляет задать более точный цикл: какой эндпойнт медленный, для каких пользователей и с какого момента? Если Duration растёт только на одном маршруте при стабильном CPU, у вас уже есть более точная отправная точка.
Полезная практика: держать RED разбитым по сервисам и топовым эндпойнтам (или ключевым RPC-методам). Это помогает отличить широкое деградирование от локальной регрессии.
RED показывает где болит. USE помогает проверить какой ресурс в этом виноват.
Примеры:
Сделайте макет сфокусированным:
Если хотите согласованный рабочий процесс на инцидент, свяжите этот раздел с инвентаризацией USE в /blog/use-method-overview, чтобы перейти от «пользователи это чувствуют» к «этот ресурс ограничивает» с меньшим количеством метаний.
Расследование производительности может разрастись до десятков графиков и гипотез за минуты. Мышление Грегга — держать фокус: ваша задача не «собирать больше данных», а задать следующий вопрос, который быстрее всего уменьшит неопределённость.
Большинство проблем задержки доминируется одной стоимостью (или небольшой парой): один горячий лок, одна медленная зависимость, один перегруженный диск, одна паттерн пауз GC. Приоритизация означает сначала искать эту доминирующую стоимость, потому что уменьшение на 5% в пяти местах редко улучшит видимую пользователю задержку.
Практическая проверка: «Что могло бы объяснить большую часть изменения задержки, которое мы видим?» Если гипотеза объясняет только крошечную долю — у неё низкий приоритет.
Используйте сверху вниз, когда отвечаете на «Пострадали ли пользователи?». Начните с эндпойнтов (сигналы в стиле RED): задержка, пропускная способность, ошибки. Так вы избежите оптимизации того, что не на критическом пути.
Используйте снизу вверх, когда хост явно «болен» (симптомы USE): насыщение CPU, неконтролируемое потребление памяти, I/O wait. Если узел загружен, время будет потрачено в бессмысленном рассмотрении персентилей эндпойнтов без понимания ограничения.
Когда срабатывает алерт, выберите ветку и оставайтесь на ней, пока не подтвердите или не опровергнете:
Ограничьте себя небольшим начальным набором сигналов, затем углубляйтесь только если что-то двинулось. Если нужен чеклист, чтобы держать фокус, привяжите шаги к ранбуку, например /blog/performance-incident-workflow, чтобы каждая новая метрика имела цель: ответить на конкретный вопрос.
Профилирование в продакшне кажется рискованным, потому что затрагивает живую систему — но часто это самый быстрый способ заменить спор доказательством. Логи и дашборды могут сказать, что медленно. Профилирование показывает, куда уходит время: какие функции горячие, какие потоки ждут и какие пути кода доминируют во время инцидента.
Профилирование — инструмент учёта времени. Вместо споров («это БД» vs «это GC») вы получаете данные вида «45% CPU-сэмплов приходятся на JSON-парсинг» или «большинство запросов блокируются на мьютексе». Это сужает следующий шаг до одного-двух конкретных исправлений.
Каждый отвечает на разный вопрос. Высокая задержка при низком CPU обычно указывает на off-CPU или время ожидания локов, а не на on-CPU-горячие точки.
Многие команды начинают с по требованию, затем переходят к всегда включённому, когда доверяют безопасности и видят повторяющиеся проблемы.
Профилирование в продакшне безопасно, когда вы контролируете стоимость. Предпочитайте сэмплинг (не трассируйте каждое событие), держите окна захвата короткими (например, 10–30 секунд) и измеряйте оверхед сначала в канаре. Если не уверены — начните с низкой частоты сэмплирования и повышайте только при необходимости.
Flame-графы визуализируют, куда ушло время во время профилирования сэмпла. Каждая «плашка» — функция (или фрейм стека), а каждый стек показывает, как выполнение дошло до этой функции. Они отличны для быстрого выявления паттернов — но не говорят автоматически «баг здесь».
Обычно flame-графы представляют on-CPU сэмплы: время, когда программа действительно выполнялась на CPU. Они выделяют CPU-горячие пути, неэффективный парсинг, чрезмерную сериализацию или участки, которые действительно жгут CPU.
Они не показывают напрямую ожидания диска, сети, планировщика или время, заблокированное на мьютексе (это off-CPU время и требует иного профилирования). Также flame-граф сам по себе не доказывает причинно-следственную связь с пользовательской задержкой, если вы не связали его с чётким сценарием.
Самую широкую плашку легко обвинить, но спросите: можно ли её изменить или это просто «время в malloc, GC или логировании», потому что реальная проблема выше по потоку? Также следите за отсутствием контекста (JIT, inlining, символы), что может сделать плашку видимым виновником, когда она лишь «вестник».
Рассматривайте flame-граф как ответ на суженный вопрос: какой эндпойнт, какое временное окно, какие хосты и что изменилось. Сравнивайте «до vs после» (или «здоровый vs деградированный») flame-графы для одного и того же пути запроса, чтобы уменьшить шум профилирования.
Когда задержки растут, многие команды в первую очередь смотрят на %CPU. Это понятно — но часто это вводит в заблуждение. Сервис может иметь «всего 20% CPU» и при этом быть ужасно медленным, если потоки большую часть времени не выполняются.
%CPU отвечает на вопрос «насколько загружен процессор?» Он не отвечает на «куда ушло время моего запроса?». Запросы могут простаивать, пока потоки ждут, блокируются или остаются приостановленными планировщиком.
Ключевая мысль: wall-clock время запроса включает и on-CPU работу, и off-CPU ожидание.
Off-CPU время обычно скрывается за зависимостями и contention:
Несколько сигналов часто коррелируют с off-CPU узкими местами:
Эти признаки говорят «мы ждём», но не чего мы ждём.
Off-CPU профайлинг атрибутирует время к причине того, почему вы не выполнялись: блокировка в системных вызовах, ожидание локов, sleep или дескетулирование. Это мощно для работы с задержками, потому что превращает расплывчатые замедления в действительные категории: «блокируется на mutex X», «ждёт read() с диска», «застрял в connect() к апстриму». Назвав ожидание, вы можете измерить его, подтвердить и исправить.
Работа с производительностью часто проваливается в одном месте: кто-то увидел подозрительную метрику, объявил её «проблемой» и начал тюнинг. Методы Грегга заставляют вас притормозить и доказать, что именно ограничивает систему, до внесения изменений.
Узкое место — ресурс или компонент, который в данный момент лимитирует пропускную способность или вызывает задержку. Если вы его облегчите, пользователи увидят улучшение.
Горячая точка — место, где тратится время (например, функция, часто встречающаяся в профиле). Горячая точка может быть реальным узким местом или просто занятостью, не влияющей на медленный путь.
Шум — всё, что выглядит значимым, но не является таковым: фоновые джобы, одноразовые пики, артефакты сэмплинга, эффекты кэширования или «топ-токеры», которые не коррелируют с видимой пользователю проблемой.
Начните с чистого снимка до: пользовательский симптом (задержка или уровень ошибок) и ведущие кандидаты (CPU-сатурация, глубина очереди, I/O диска, contention локов и т.д.). Затем внесите контролируемое изменение, которое, по идее, должно повлиять только на подозреваемую причину.
Примеры причинно-следственных тестов:
Корреляция — это подсказка, а не вердикт. Если «CPU растёт вместе с задержкой», проверьте, изменится ли задержка при изменении доступности CPU или сокращении CPU-работы.
Запишите: что измеряли, какое точное изменение внесли, до/после результаты и наблюдаемое улучшение. Это превращает одноразовый успех в переиспользуемый плейбук для следующего инцидента и предотвращает переписывание истории интуицией.
Инциденты по производительности ощущаются как срочные, и именно тогда чаще всего появляется гадание. Лёгкий повторяемый рабочий процесс помогает перейти от «что-то медленное» к «мы знаем, что изменилось» без метаний.
Обнаружить: алертить по пользовательской задержке и уровню ошибок, а не только по CPU. Пейджить, когда p95/p99 задержка пересекает порог в устоявшемся окне.
Классифицировать: сразу ответьте на три вопроса: что медленно, когда началось и кто пострадал? Если вы не можете назвать область (сервис, эндпойнт, регион, когорта), вы не готовы оптимизировать.
Измерить: собрать доказательства, сужающие узкое место. Предпочитайте захваты, ограниченные по времени (например, 60–180 секунд), чтобы сравнить «плохо» и «хорошо».
Исправить: меняйте одну вещь за раз, затем снова измеряйте те же сигналы, чтобы подтвердить улучшение и исключить плацебо.
Держите общий дашборд, которым пользуются все во время инцидентов. Пусть он будет скучным и однообразным:
Цель не в том, чтобы графать всё; цель — сократить время до первого факта.
Инструментируйте эндпойнты, которые имеют значение (checkout, login, search), а не каждый эндпойнт. Для каждого согласуйте: ожидаемый p95, максимальный уровень ошибок и ключевую зависимость (БД, кэш, сторонний сервис).
До следующего простоя договоритесь о комплекте захвата:
Задокументируйте это в коротком ранбуке (например, /runbooks/latency), включая кто может запускать захваты и где хранятся артефакты.
Методология Грегга по сути про контролируемые изменения и быструю верификацию. Если ваша команда строит сервисы с помощью Koder.ai (чат-ориентированная платформа для генерации и итерации web, backend и mobile-приложений), две функции хорошо соотносятся с этим подходом:
Даже если вы не генерируете код во время инцидента, эти привычки — маленькие диффы, измеримые результаты и быстрая обратимость — это те же принципы, которые пропагандирует Грегг.
10:15 утра — дашборд показывает рост p99 задержки API с ~120 мс до ~900 мс во время пика. Уровень ошибок стабильный, но пользователи жалуются на «медленные» запросы.
Начните с сервиса: Rate, Errors, Duration.
Разбейте Duration по эндпойнтам и увидите, что один маршрут доминирует в p99: POST /checkout. Rate вырос в 2×, ошибок нет, но Duration растёт именно при увеличении конкурентности. Это указывает на очереди или contention, а не на явную ошибку.
Далее проверьте, является ли задержка вычислительным временем или временем ожидания: сравните «время обработчика» приложения и общее время запроса (или upstream vs downstream спаны, если есть трассировка). Время обработчика низкое, общее время высоко — запросы ждут.
Инвентаризация потенциальных узких мест: Utilization, Saturation, Errors по CPU, памяти, диску и сети.
Загрузка CPU ~35%, но run queue и контекстные переключения растут. Диск и сеть выглядят спокойно. Это несоответствие (низкий %CPU, высокий wait) — классический намёк: потоки не сжигают CPU — они блокированы.
Вы сняли off-CPU профиль во время спайка и обнаружили много времени в мьютексе вокруг общего кэша «promotion validation».
Заменили глобальный лок на лок по ключу (или добавили lock-free путь для чтения), задеплоили и наблюдаете, как p99 возвращается к базовой линии при той же Rate.
Чеклист пост-инцидента: