«Тормозит» это не диагноз. Задача инженера пройти от симптома (вырос p99) к конкретной причине (функция сериализации тратит 40% CPU, или 80 запросов в базу вместо одного) и подтвердить её измерением, а не догадкой.
Перцентили и хвост
- Распределение задержек почти всегда с длинным хвостом: p50 говорит о типичном запросе, p99 и p99.9 о худших, которые и запоминают пользователи.
- Усиление хвоста: если страница делает 100 параллельных вызовов, и каждый в 1% случаев медленный, то хотя бы один медленный будет в 1 - 0.99^100 ≈ 63% загрузок страницы. Поэтому у бэкендов с широким разветвлением p99 важнее p50.
- Борьба с хвостом: таймауты и дедлайны, хеджированные запросы (повторный запрос к другой реплике, если первый не ответил за p95), ограничение очередей, вынос тяжёлой работы из горячего пути.
Методика
- Симптом: какой SLI ухудшился, с какого времени, для каких запросов (RED).
- Где время: трейсы медленных запросов, собственное время спанов против ожидания зависимостей.
- Ресурсы: USE по CPU, памяти, диску, сети и по программным ресурсам (пулы соединений, потоки, блокировки).
- Профиль: если время уходит внутри сервиса, профилировщик покажет, на какие функции.
- Гипотеза и проверка: изменить одно, измерить снова.
Классика Брендана Грегга для Linux-узла за первые минуты: uptime (load average), dmesg | tail (OOM, ошибки), vmstat 1, mpstat -P ALL 1, pidstat 1, iostat -xz 1, free -m, sar -n DEV 1, sar -n TCP,ETCP 1, top.
Профилировщики
Сэмплирующий профилировщик много раз в секунду снимает стек выполнения. Функции, которые чаще оказываются на стеке, тратят больше времени. Накладные расходы малы, поэтому его можно запускать в проде.
- Go:
net/http/pprof,go tool pprof http://host/debug/pprof/profile?seconds=30; профили CPU, heap, goroutine, mutex, block. - Java: async-profiler, Java Flight Recorder (JFR).
- Python: py-spy подключается к работающему процессу без перезапуска:
py-spy top --pid,py-spy record -o profile.svg --pid. - Node.js:
--cpu-prof, инспектор, clinic.js. - Linux в целом:
perf record -gиperf report, eBPF-инструменты (bpftrace, BCC:runqlat,biolatency,offcputime).
Непрерывное профилирование (Grafana Pyroscope, Parca) постоянно снимает профили со всех экземпляров с малыми накладными расходами. Можно сравнить профиль до и после релиза и найти регрессию.
Flame graph
Визуализация стеков: каждый прямоугольник это функция, по вертикали глубина стека (вызывающая ниже, вызываемая выше), ширина это доля сэмплов, в которых функция была на стеке. Ось X не время: функции на одном уровне отсортированы по алфавиту. Ищут широкие «плато» наверху: функции, которые сами тратят много времени.
- On-CPU профиль показывает, где сжигается процессор.
- Off-CPU показывает, где поток ждёт: блокировки, ввод-вывод, сон. Если сервис медленный, а CPU почти не загружен, нужен именно off-CPU анализ или трейсы.
Частые причины медленности
- N+1 запросов к базе в цикле.
- Ожидание пула: соединений к базе меньше, чем одновременных запросов (закон Литтла).
- Троттлинг CPU в контейнере: лимит CPU исчерпан внутри периода квоты, процесс замораживается до следующего периода. Средняя загрузка низкая, а задержки большие. Метрика
container_cpu_cfs_throttled_periods_total. - Паузы сборщика мусора и избыточные аллокации.
- Блокировки (mutex contention) и последовательная работа там, где можно параллельно.
- Сериализация больших JSON, регулярные выражения с катастрофическим перебором.
- Синхронные вызовы медленных зависимостей без таймаутов.
Закон Амдала
Если долю p работы можно распараллелить на n потоков, ускорение не больше 1 / ((1 - p) + p / n). При p = 0.9 и 10 потоках ускорение около 5.3, а не 10: последовательная часть ограничивает всё. Поэтому оптимизировать надо самое широкое место профиля, а не то, что проще.
Осторожно в проде
straceна горячем процессе может замедлить его в разы (он работает через ptrace). eBPF-инструменты иperfгораздо безопаснее.- Дамп кучи большого JVM-процесса останавливает его на секунды и пишет гигабайты.
- Профилируйте один экземпляр, выведя его из ротации, если сомневаетесь.
Частые ошибки
- Оптимизировать «на глаз» без профиля.
- Смотреть только на средние задержки.
- Считать, что раз CPU 30%, проблема не в CPU (троттлинг, один перегруженный поток).
- Читать ось X flame graph как время.
- Бенчмарк на ноутбуке вместо профиля прода.
Что спросят на собесе
- Сервис стал медленнее после релиза: как будете искать.
- Как читать flame graph.
- Что такое on-CPU и off-CPU профилирование.
- Почему p99 важнее среднего и что такое усиление хвоста.
- Что такое CPU throttling в Kubernetes и как его увидеть.