Что такое NUMA и почему это важно на двух процессорах
NUMA (Non-Uniform Memory Access) — архитектура памяти двухпроцессорного сервера, где у каждого CPU есть "своя" память с быстрым доступом и "чужая" память другого сокета с доступом через межпроцессорную шину. Тема актуальна в первую очередь для серверов с двумя и более сокетами — то, как выбран процессор, напрямую определяет число узлов NUMA и их размер.
Если приложение не учитывает NUMA, планировщик ОС может разместить процесс на одном сокете, а память выделить на другом. Результат — просадка производительности без видимой причины в мониторинге CPU.
Как посмотреть топологию NUMA
lscpu | grep -i numa
numactl --hardware
Команда numactl --hardware покажет количество узлов, объём памяти на каждом и расстояния между узлами (node distances). Значение 10 — обращение к своему узлу, 20-21 — к соседнему: чем выше число, тем дороже промах.
Проблема: память "не своего" узла
Типичный сценарий: сервер на двух процессорах, 256 ГБ памяти, приложение запущено без привязки. Планировщик Linux переносит процесс между ядрами разных сокетов, а выделенная память остаётся на первом узле. Каждое обращение ко второму сокету идёт через межпроцессорный интерконнект, и задержка растёт.
Это особенно заметно на серверах баз данных, где кэш и буферы должны жить рядом с ядрами, которые их читают тысячи раз в секунду.
Привязка процессов: numactl и cpuset
Явная привязка процесса к узлу решает проблему напрямую:
numactl --cpunodebind=0 --membind=0 /usr/bin/mysqld
numactl --interleave=all /usr/bin/myapp
Флаг --cpunodebind закрепляет процесс за ядрами узла 0, --membind — за памятью того же узла. Флаг --interleave=all распределяет память равномерно по всем узлам — полезно для приложений, которые сами не умеют работать с NUMA и не выигрывают от локальности.
| Параметр | Что делает | Когда использовать |
|---|---|---|
| --cpunodebind | Закрепляет ядра узла | СУБД, JVM с большой кучей |
| --membind | Закрепляет память узла | Вместе с cpunodebind |
| --interleave=all | Чередует память по узлам | NUMA-неосведомлённые приложения |
| --preferred | Предпочитает узел, но не запрещает другие | Гибкая нагрузка |
NUMA в базах данных и виртуализации
PostgreSQL и MySQL рекомендуют запускать один инстанс на NUMA-узел при больших объёмах памяти либо явно привязывать процесс через numactl, если инстанс один на весь сервер. В виртуализации (KVM, Proxmox) стоит выделять виртуальной машине vCPU и память в пределах одного физического узла — иначе гипервизор столкнётся с той же проблемой межузлового трафика.
Перед изменением привязки измерьте эффект: прогоните тест до и после через sysbench с нагрузкой, близкой к реальной, а не синтетической однопоточной.
Чек-лист настройки NUMA
- Проверьте topology через numactl --hardware до тюнинга, не после.
- Привязывайте процесс к CPU и памяти вместе, а не по отдельности.
- Для NUMA-неосведомлённых приложений используйте --interleave=all вместо ручной привязки.
- Замеряйте задержки и throughput до и после — прирост не всегда очевиден на слабой нагрузке.
Частые ошибки при работе с NUMA
numactl: Warning: cpu binding outside of valid numa node cpuset
libnuma: Warning: node argument 4 is out of range
mbind: Cannot allocate memory
numactl: command not found
| Сообщение | Причина | Решение |
|---|---|---|
| cpu binding outside of valid numa node cpuset | Указанные ядра не принадлежат тому узлу NUMA, для которого задан --membind | Сверить номера ядер узла через numactl --hardware перед составлением команды |
| node argument N is out of range | В команде указан номер узла, которого физически нет на сервере (например, узел 4 на двухпроцессорной машине с узлами 0 и 1) | Проверить реальное число узлов: numactl --hardware | grep available |
| mbind: Cannot allocate memory | На запрошенном узле NUMA физически не хватает свободной памяти под жёсткий --membind | Использовать --preferred вместо --membind, чтобы разрешить выделение на другом узле при нехватке |
| numactl: command not found | Пакет numactl не установлен — он не входит в минимальный набор большинства дистрибутивов | Установить пакет: apt install numactl или dnf install numactl |
Способы управления NUMA-привязкой: сравнение
| Способ | Уровень применения | Когда использовать |
|---|---|---|
| numactl (для одного процесса) | Один запускаемый процесс | Быстрая проверка гипотезы и ручной запуск СУБД или сервиса |
| cgroups cpuset | Группа процессов, systemd-юнит, контейнер | Постоянная привязка сервиса, который перезапускается системой, а не вручную |
| BIOS Node Interleaving | Вся система целиком | Только временная диагностика — полностью отключает NUMA, не для продакшна |
Когда привязка NUMA не даёт эффекта
- Сервер однопроцессорный — топология NUMA вырождена в один узел, и
numactl --hardwareпокажет node distance только 10, привязывать нечего - Рабочий набор данных приложения меньше памяти одного узла NUMA — планировщик и так почти всегда держит процесс и память рядом без ручного вмешательства
- Нагрузка сильно многопоточная и равномерно использует все ядра сервера — жёсткая привязка к одному узлу в этом случае снижает параллелизм вместо того, чтобы его ускорить
- Виртуальная машина уже мигрирует между узлами кластера (live migration) — статическая привязка vCPU к NUMA-узлу физического сервера теряет смысл при переезде на другую машину
Проверка результата
# Локальные и удалённые обращения к памяти по узлам
numastat -p $(pgrep -f mysqld)
# Задержка доступа к памяти до и после привязки
numactl --cpunodebind=0 --membind=0 -- perf stat -e task-clock,cycles ./benchmark
# Итоговое распределение памяти процесса по узлам
cat /proc/$(pgrep -f mysqld)/numa_maps | head -5
- Выполните
numastat -p PIDдо и после привязки — доля numa_foreign и other_node должна заметно снизиться - Замерьте целевую метрику нагрузки (запросы в секунду, время ответа) обоими способами — без привязки и с ней — на одинаковом профиле трафика
- Проверьте через
/proc/PID/numa_maps, что основной объём страниц процесса действительно лежит на ожидаемом узле, а не распределён поровну - Повторите замер через день работы под реальной нагрузкой — синтетический тест сразу после запуска не всегда показывает то же поведение, что установившийся прогрев кэшей
Настройку самой виртуализации, где NUMA-топология физического сервера пробрасывается в гостевые машины, разбирает статья виртуализация на выделенном сервере.