# NUMA на двух процессорах: привязка процессов и память

> NUMA на двухпроцессорном сервере влияет на задержку памяти: разбираем топологию узлов, привязку процессов через numactl и типичные ошибки настройки баз данных.

## Что такое NUMA и почему это важно на двух процессорах

NUMA (Non-Uniform Memory Access) — архитектура памяти двухпроцессорного сервера, где у каждого CPU есть "своя" память с быстрым доступом и "чужая" память другого сокета с доступом через межпроцессорную шину. Тема актуальна в первую очередь для серверов с двумя и более сокетами — то, как [выбран процессор](https://www.zevshost.net/ru/kb/vybor-protsessora-servera/), напрямую определяет число узлов NUMA и их размер.

Если приложение не учитывает NUMA, планировщик ОС может разместить процесс на одном сокете, а память выделить на другом. Результат — просадка производительности без видимой причины в мониторинге CPU.

## Как посмотреть топологию NUMA

```
lscpu | grep -i numa
numactl --hardware
```

Команда numactl --hardware покажет количество узлов, объём памяти на каждом и расстояния между узлами (node distances). Значение 10 — обращение к своему узлу, 20-21 — к соседнему: чем выше число, тем дороже промах.

## Проблема: память "не своего" узла

Типичный сценарий: сервер на двух процессорах, 256 ГБ памяти, приложение запущено без привязки. Планировщик Linux переносит процесс между ядрами разных сокетов, а выделенная память остаётся на первом узле. Каждое обращение ко второму сокету идёт через межпроцессорный интерконнект, и задержка растёт.

Это особенно заметно на [серверах баз данных](https://www.zevshost.net/ru/kb/server-pod-bazu-dannyh/), где кэш и буферы должны жить рядом с ядрами, которые их читают тысячи раз в секунду.

## Привязка процессов: 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](https://www.zevshost.net/ru/kb/benchmark-servera-fio-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
```

1. Выполните `numastat -p PID` до и после привязки — доля **numa\_foreign** и **other\_node** должна заметно снизиться
2. Замерьте целевую метрику нагрузки (запросы в секунду, время ответа) обоими способами — без привязки и с ней — на одинаковом профиле трафика
3. Проверьте через `/proc/PID/numa_maps`, что основной объём страниц процесса действительно лежит на ожидаемом узле, а не распределён поровну
4. Повторите замер через день работы под реальной нагрузкой — синтетический тест сразу после запуска не всегда показывает то же поведение, что установившийся прогрев кэшей

Настройку самой виртуализации, где NUMA-топология физического сервера пробрасывается в гостевые машины, разбирает статья [виртуализация на выделенном сервере](https://www.zevshost.net/ru/kb/virtualizatsiya-na-vydelennom-servere/).

---

Источник: https://www.zevshost.net/ru/kb/numa-privyazka-processov/
Обновлено: 2026-09-30
