MAATRIX / Блог / Нагрузка выросла, а сервер простаивает: рост, которого не видно в метриках

Нагрузка выросла, а сервер простаивает: рост, которого не видно в метриках

MAATRIX

Число клиентов растёт, воронка продаж не жалуется, а график top или дашборд в Grafana показывает те же спокойные 15-20% загрузки CPU, что и полгода назад. Возникает соблазн решить, что инфраструктура просто хорошо спроектирована и рост её пока не касается — и это иногда правда, но чаще означает, что рост идёт не там, где смотрит стандартная приборная панель. CPU и память — это метрики про вычисления, а бизнес может расти по трём другим осям: числу соединений, объёму данных и количеству фоновых задач, — и все три способны молчать до тех пор, пока не упрутся в жёсткий потолок разом.

Почему CPU и память ничего не говорят о реальном росте

Загрузка процессора отражает, сколько времени ядра тратят на вычисления: разбор запроса, выполнение SQL, рендеринг ответа. Память отражает, сколько данных приложение держит в оперативном доступе. Обе метрики честны в своих рамках, но у них есть слепая зона: они ничего не говорят о том, сколько у вас *открытых состояний* — соединений, файловых дескрипторов, записей в очередях, — если поддержание этих состояний само по себе не требует вычислений.

Классический пример — тысяча открытых, но почти неактивных TCP-соединений. Каждое из них занимает файловый дескриптор, немного памяти ядра под структуру сокета и буферы, но пока по нему не идёт трафик, оно не потребляет ни такта CPU. Сервер может держать десятки тысяч таких соединений с загрузкой процессора, неотличимой от простоя, — а затем упереться не в CPU, а в лимит открытых файлов на процесс, и начать отказывать в новых подключениях с ошибкой EMFILE, пока старая нагрузка выглядит на графике так же ровно, как всегда.

То же самое с данными: таблица в базе может расти месяцами, не создавая заметной дополнительной нагрузки на CPU — SELECT по индексу стоит примерно одинаково что на миллионе, что на десяти миллионах строк, пока индекс помещается в память и план запроса не меняется. А фоновые задачи по определению эпизодичны: cron, который раз в час пробегает по всей таблице, создаёт всплеск нагрузки длиной в несколько минут раз в 60 — если метрика CPU усреднена по пяти минутам или дольше, такой всплеск размывается в общем графике почти до незаметности.

Итог: отсутствие роста на графике CPU/RAM — это не доказательство того, что инфраструктура справляется с ростом бизнеса. Это доказательство того, что рост (если он есть) не проявляется именно в вычислениях. Дальше — по порядку, где его чаще всего прячет.

Рост числа соединений, а не вычислительной нагрузки

Каждый новый пользователь, интеграция или микросервис — это не только запросы, которые нужно обработать, но и соединения, которые нужно держать открытыми. У современных протоколов это особенно заметно: HTTP-клиенты с keep-alive, WebSocket-подключения, долгоживущие соединения к базе через пул, вебхуки от внешних сервисов, которые держат сокет открытым в ожидании ответа. Всё это добавляется в счётчик открытых соединений почти без следа на CPU.

Первый шаг — вообще увидеть эту цифру, а не гадать по ощущениям:

ss -s
Total: 14823
TCP:   14210 (estab 9840, closed 210, orphaned 3, timewait 180)

Строка estab — это те самые активные, установленные соединения. Если она растёт неделя за неделей быстрее, чем растёт число обрабатываемых в секунду запросов (которое проще всего смотреть по логам веб-сервера или по метрикам приложения), это и есть сигнал: клиенты стали держать соединения дольше или их стало банально больше на одного пользователя, а не просто выросла частота запросов. Разбить соединения по состояниям и найти, где именно накапливается лишнее — в статье про то, что реально показывают ss и netstat.

Отдельно стоит смотреть не только на количество соединений, но и на их буферы — очереди Send-Q и Recv-Q, которые видны в том же выводе ss -tan. Растущая, не убывающая очередь Recv-Q у конкретного соединения означает, что данные пришли, но приложение их не забирает — то есть где-то на этом конкретном соединении нагрузка уже реальна, просто её не видно в среднем CPU по серверу, потому что затронут один процесс или даже одно соединение из тысяч. Как читать эти два столбца и на что они указывают в разных ситуациях — разобрано в статье про очереди Send-Q и Recv-Q.

Практический ориентир, который стоит завести у себя: снимайте ss -s раз в день (можно через cron в лог-файл или в текстовый канал мониторинга) и сравнивайте число estab неделя к неделе. Если оно растёт быстрее выручки или числа активных пользователей — вы нашли канал невидимого роста раньше, чем он превратился в отказ по лимиту соединений.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Файловые дескрипторы и лимиты — где рост соединений упирается в стену

Рост числа соединений не бесконечен не потому, что кончается CPU, а потому, что у каждого процесса в Linux есть лимит на число одновременно открытых файловых дескрипторов — а сокет тоже дескриптор. Посмотреть текущий лимит для процесса и для сессии:

ulimit -n
cat /proc/<PID>/limits | grep "Max open files"

Посмотреть, сколько дескрипторов реально занято прямо сейчас в системе:

cat /proc/sys/fs/file-nr

Первое число в выводе — сколько дескрипторов выделено сейчас, второе — сколько свободно из зарезервированных, третье — системный максимум (fs.file-max). На уровне конкретного процесса то же самое проще посмотреть так:

ls /proc/<PID>/fd | wc -l

Ключевая проблема с этим лимитом в том, что он не связан с CPU вообще: процесс может простаивать по загрузке процессора и одновременно быть в одном шаге от отказа новых подключений, потому что дескрипторы кончаются. Разница между «сервер справляется» и «сервер начал отказывать новым клиентам с ошибкой открытия сокета» здесь измеряется не процентами загрузки, а буквально одной цифрой — свободными слотами до лимита, и эта цифра нигде не появляется на стандартном дашборде CPU/RAM, если её туда не добавить отдельно. Как считать, сколько дескрипторов тратит один типичный запрос, и как выставлять лимиты с запасом под рост, а не впритык — в статье про настройку лимитов открытых файлов.

Практически это значит: если у вас растёт число активных клиентов, интеграций или фоновых воркеров, лимит открытых файлов стоит поднимать заранее, ориентируясь на прогноз роста соединений, а не постфактум — после первого Too many open files в логах, когда часть запросов уже отвалилась.

Объём данных растёт медленнее, чем это заметно — до порога

Второй канал невидимого роста — объём данных в базе, на диске, в логах. Он коварен тем, что его влияние на производительность нелинейно: пока таблица помещается в оперативную память сервера базы данных (или хотя бы её рабочий индекс), рост от миллиона до пяти миллионов строк почти не сказывается на времени отклика типичного запроса — план выполнения тот же, чтение идёт из кеша, CPU не растёт заметно. А затем данные перестают помещаться в память, план запроса меняется, диск начинает читаться чаще операционной системой, и производительность падает не постепенно, а скачком — иногда за одну неделю роста, которая внешне ничем не отличалась от предыдущих пятидесяти.

Отслеживать стоит не место на диске в процентах (эта метрика тоже растёт линейно и не предупреждает заранее), а конкретный объём данных по ключевым таблицам и соотношение этого объёма с доступной памятью под кеш:

-- PostgreSQL: размер базы и топ таблиц по размеру
SELECT pg_size_pretty(pg_database_size('mydb'));
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;
-- MySQL: аналогичный срез по размеру таблиц
SELECT table_name,
       ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'mydb'
ORDER BY (data_length + index_length) DESC
LIMIT 10;

Полезнее всего смотреть не на абсолютное число, а на скорость его изменения — прирост в мегабайтах или гигабайтах в неделю по ключевым таблицам, и сопоставлять его с ростом выручки или числа активных пользователей. Если база растёт заметно быстрее бизнес-показателей — это отдельный, самостоятельный симптом, который стоит анализировать так же внимательно, как рост соединений: где накапливаются логи, дублируются записи, не работает архивация старых данных или ретеншн. Пока порог «не помещается в память» не пройден, это не видно ни в CPU, ни в задержке ответа — а после того как пройден, обе метрики меняются резко и одновременно, и разбираться приходится уже в аварийном режиме, а не спокойно.

Фоновые задачи и очереди: пики, которых нет в усреднённых графиках

Третий канал — рост числа и частоты фоновых процессов: cron-задач, периодических воркеров, очередей на обработку писем, уведомлений, экспорта отчётов. Каждая отдельная задача может быть короткой и лёгкой — обработать пачку писем, пересчитать агрегат, отправить уведомления. Проблема не в тяжести одной задачи, а в их количестве и в том, как они накладываются друг на друга во времени.

Если метрика CPU в мониторинге усреднена по 1-5-минутным интервалам (а это стандартная настройка почти везде), задача, которая грузит одно ядро на 100% в течение 20 секунд раз в минуту, на графике выглядит как ровная линия в районе 5-10% — усреднение съедает пик почти полностью. По мере роста бизнеса таких задач становится больше: добавляется новая интеграция — добавляется своя периодическая синхронизация, растёт число пользователей — растёт число писем в очереди рассылки, появляется новый отчёт — появляется свой ночной пересчёт. Каждая по отдельности незаметна на усреднённом графике, а вместе они всё чаще пересекаются по времени и создают уже не короткий, а затяжной пик, который тоже может не попасть в интервал усреднения, если он приходится, скажем, на 2-3 часа ночи, когда никто не смотрит на графики в реальном времени.

Смотреть в этом случае нужно не на усреднённый CPU, а на два конкретных показателя: реальное время выполнения фоновых задач (растёт ли оно от запуска к запуску) и глубину очередей, если задачи идут через очередь, а не напрямую по расписанию:

# systemd timers: список и последний результат запуска
systemctl list-timers --all

# длина очереди в Redis (пример для списка-очереди)
redis-cli LLEN queue:emails

# то же самое для очереди задач Celery через Redis
redis-cli LLEN celery

Растущая от замера к замеру глубина очереди — прямой сигнал, что задачи создаются быстрее, чем обрабатываются, даже если сам воркер выглядит на графике CPU не загруженным (он может простаивать между короткими рывками обработки). Разбор того, с какого момента наложение фоновых задач друг на друга становится системной проблемой, а не совпадением, — в статье про то, где начинается наложение при росте числа cron-задач.

Что отслеживать помимо CPU и памяти

Практический вывод из всех трёх разделов выше один: стандартная пара CPU + память — необходимое, но не достаточное условие мониторинга растущей системы. Ниже — минимальный набор дополнительных показателей, которые стоит снимать регулярно именно потому, что рост чаще всего проявляется в них раньше, чем в загрузке процессора.

ПоказательКак посмотретьНа что указывает рост
Число установленных TCP-соединенийss -s, `ss -tan state established \wc -l`Растёт число клиентов/интеграций или соединения стали жить дольше
Занятые файловые дескрипторы на процесс`ls /proc/<PID>/fd \wc -l, ulimit -n`Приближение к лимиту, не связанное с CPU
Размер ключевых таблиц/базыpg_total_relation_size, information_schema.tablesДанные растут быстрее бизнес-показателей, риск выхода за объём кеша
Глубина очередей фоновых задачredis-cli LLEN, метрики брокера (RabbitMQ, Celery)Задачи создаются быстрее, чем обрабатываются
Время выполнения периодических jobлоги cron/systemd timer, история запусковЗадачи стали тяжелее при том же расписании

Ни один из этих показателей не заменяет CPU и память — он их дополняет. Если у вас уже настроен Prometheus с node_exporter, часть этого закрывается готовыми метриками (node_filefd_allocated, node_sockstat_TCP_alloc, метрики размера таблиц через postgres_exporter или mysqld_exporter), и задача сводится не к сбору новых данных, а к тому, чтобы добавить эти графики на дашборд рядом с CPU, а не держать их только «на случай если понадобится». Разумный минимум — раз в неделю смотреть на пять показателей из таблицы выше в одном месте, даже если по CPU и памяти всё спокойно: именно тогда, когда стандартная панель молчит, стоит проверять то, что она не показывает.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Если CPU и память стабильно низкие, точно ли можно расслабиться?

Нет — это значит только то, что рост (если он есть) не идёт через вычислительную нагрузку. Стоит отдельно проверить число соединений, объём данных и глубину очередей фоновых задач — рост может идти именно там, оставаясь незаметным на панели CPU/RAM сколь угодно долго.

С какой периодичностью снимать дополнительные метрики — соединения, дескрипторы, очереди?

Для большинства проектов достаточно ежедневного среза (по cron в лог или в Prometheus, если он уже развёрнут) и еженедельного взгляда на тренд. Резкие изменения между соседними днями стоит смотреть чаще — раз в час, если метрика уже показала тревожную динамику.

Как понять, что рост базы данных приближается к опасному порогу?

Ориентир — соотношение размера рабочих таблиц (или их активно используемых индексов) с объёмом оперативной памяти, выделенной под кеш СУБД. Пока данные заметно меньше доступной памяти под кеш, риск невысок; по мере приближения к этому объёму стоит следить за трендом чаще, потому что деградация после выхода за порог обычно наступает быстро, а не постепенно.

Почему усреднённые графики мониторинга скрывают пики от фоновых задач?

Потому что усреднение по интервалу (типично 1-5 минут) математически размывает короткий, но интенсивный всплеск нагрузки в общем среднем значении за этот интервал. Задача, грузящая ядро на 100% двадцать секунд из шестидесяти, в среднем по минуте даёт около 30%, а если интервал усреднения больше — цифра будет ещё скромнее. Разглядеть такие пики можно только через детализацию с меньшим шагом или через прямые метрики самих задач (длительность выполнения, глубина очереди), а не через усреднённый CPU.

С чего начать, если ни одна из этих метрик сейчас не собирается?

С самой дешёвой — числа соединений через ss -s, снимаемого раз в день в текстовый лог. Это одна команда без дополнительной инфраструктуры, и уже недельный тренд по ней часто показывает, есть ли вообще невидимый рост, прежде чем вкладываться в настройку полноценного мониторинга по всем пяти показателям сразу.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →