Що таке 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-топологія фізичного сервера прокидається у гостьові машини, розглядає стаття віртуалізація на виділеному сервері.