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

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

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

NUMA (Non-Uniform Memory Access) — архітектура пам'яті двопроцесорного сервера, де у кожного CPU є "своя" пам'ять зі швидким доступом і "чужа" пам'ять іншого сокета з доступом через міжпроцесорну шину. Тема актуальна насамперед для серверів з двома і більше сокетами — те, як [обрано процесор](https://www.zevshost.net/uk/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/uk/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/uk/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/uk/kb/virtualizatsiya-na-vydelennom-servere/).

---

Джерело: https://www.zevshost.net/uk/kb/numa-privyazka-processov/
Оновлено: 2026-09-30
