Что такое 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 до и после — прирост не всегда очевиден на слабой нагрузке.