С HugePages и THP у админов традиционно два вопроса: включать или выключать, и для кого именно. На VDS ошибки настройки быстро превращаются в хвост латентности и резкие провалы производительности. Разберёмся, чем отличаются классические HugePages от Transparent Huge Pages, когда они полезны для PHP и СУБД (MySQL/MariaDB, PostgreSQL), как посчитать объём, включить без боли и проверить результат.
Что такое HugePages и чем они отличаются от THP
Классические HugePages (hugetlb) — это заранее зарезервированные страницы памяти увеличенного размера, чаще всего 2 МБ (реже 1 ГБ при соответствующей настройке ядра). Они не свопятся и выделяются из отдельного пула, который вы настраиваете через vm.nr_hugepages. Приложение явно просит такие страницы (через MAP_HUGETLB) — поэтому выигрыш предсказуем, а накладные расходы на раздувание/дефрагментацию отсутствуют.
Transparent Huge Pages (THP) — прозрачный механизм ядра, который пытается сам подменять обычные 4 КБ страницы на большие (2 МБ), чтобы снизить TLB-миссы, не требуя от приложения изменений. Красиво на бумаге, но у THP есть цена: фоновая и иногда синхронная дефрагментация памяти, из-за чего у латентно-чувствительных нагрузок возникают «пилы» и случайные задержки. Условно: THP удобно «включил и забыл», но для баз данных так делать нельзя.
Правило большого пальца: СУБД не любят THP. Для них — THP «never» и по возможности явные HugePages. Для PHP — THP «madvise» или «never», а вот для OPcache/JIT полезны именно явные HugePages.
Когда включать и что именно: PHP, MySQL/MariaDB, PostgreSQL
PHP (PHP-FPM, OPcache, JIT)
У PHP два сценария:
- OPcache: директива
opcache.huge_code_pages=1просит у ядра явные HugePages. Это даёт стабильный выигрыш на больших проектах с тёплым кешем. Требуются зарезервированные 2 МБ-страницы. - JIT (PHP 8+): JIT-буфер также может жить в больших страницах. Смысл тот же: меньше TLB-миссов, более предсказуемая производительность.
THP для обычной памяти PHP может быть и полезен, и вреден — всё зависит от шаблона аллокаций и нагрузок. Безопасный компромисс — режим madvise (приложения смогут просить THP через madvise()), а если вы боретесь за минимальную латентность p95/p99 — ставьте never.
MySQL/MariaDB
Рекомендация индустрии много лет неизменна: отключайте THP (never). Для InnoDB имеет смысл пробовать явные HugePages, включив innodb_use_large_pages и заранее зарезервировав нужное количество страниц. Важный нюанс — размер innodb_buffer_pool_size должен быть кратен 2 МБ, иначе часть пула уйдёт в обычные страницы, что сведёт пользу на нет.
Если вы параллельно отстраиваете отказоустойчивость, пригодится материал про настройку GTID и semi-sync фейловера MySQL — посмотрите Гайд по GTID и semi-sync фейловеру. А для стратегии восстановления до точки во времени есть практикум по binlog/GTID — PITR на MySQL/MariaDB.
PostgreSQL
PostgreSQL поддерживает явные HugePages для сегмента общей памяти (shared memory). Включается параметром huge_pages в postgresql.conf (значения try или on). THP — отключить (never): иначе возможны стоп-миры на дефрагментации и скачки задержек при вакууме или интенсивной буферизации.
Проверка текущего состояния THP и HugePages
Быстрые команды для диагностики:
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag
cat /proc/meminfo | egrep "Huge|AnonHuge"
Ищем, активен ли THP (квадратные скобки вокруг текущего режима), есть ли выделенные HugePages_Total и сколько из них свободно (HugePages_Free). Если AnonHugePages велик, THP активно склеивает память.
Отключаем THP корректно
Разово до перезагрузки:
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
Постоянно через systemd-юнит (надёжнее, чем редактировать init-скрипты дистрибутива):
sudo tee /etc/systemd/system/disable-thp.service > /dev/null << 'EOF'
[Unit]
Description=Disable Transparent Huge Pages
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'
ExecStart=/usr/bin/bash -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now disable-thp.service
Проверьте, что после перезагрузки режим сохранится.

Резервируем явные HugePages (2 МБ) для PHP/БД
Классический путь — настроить vm.nr_hugepages. Сначала оцените, кому и сколько нужно:
- PHP OPcache: примерно
opcache.memory_consumption+opcache.jit_buffer_size(если JIT включён), округлить вверх до 2 МБ-шагов. - PostgreSQL: ориентируйтесь на объём общей памяти, которую хотите помещать в большие страницы. Обычно это
shared_buffers(целиком) и иногда дополнительные сегменты, если используете расширения. - MySQL/MariaDB: целевой объём —
innodb_buffer_pool_size(или его значительная часть). Кратно 2 МБ.
Формула для количества страниц: ceil(Нужные_байты / 2097152). Пример: для 2 ГБ нужно ceil(2147483648 / 2097152) = 1024 страницы.
Задаём число страниц и фиксируем конфигом:
echo 'vm.nr_hugepages=1024' | sudo tee /etc/sysctl.d/60-hugepages.conf
sudo sysctl --system
cat /proc/meminfo | egrep "HugePages_Total|HugePages_Free|Hugepagesize"
Если HugePages_Total меньше ожидаемого, ядру не удалось выделить запрошенный объём (фрагментация). Увеличьте число постепенно или перезагрузитесь, затем примените настройку как можно раньше в загрузке. Помните: явные HugePages не свопятся — не резервируйте их «впритык», оставляйте запас под остальную память ОС и приложений.

PHP: включаем OPcache на HugePages
Добавьте в конфигурацию PHP (например, /etc/php.d/10-opcache.ini или профиль для используемой версии):
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.jit_buffer_size=64M
opcache.huge_code_pages=1
Перезапустите пул PHP-FPM и проверьте статус OPcache. При достаточном количестве HugePages ошибки в логах отсутствуют, а общее потребление OPcache укладывается в 2 МБ-кванты. Для косвенной проверки загляните в /proc/<pid_php-fpm>/smaps — сегменты, выделенные на hugetlb, будут видны, или используйте вывод php -i на предмет включённого opcache.huge_code_pages.
PostgreSQL: huge_pages и расчёт
В postgresql.conf включите:
huge_pages = on
shared_buffers = 4GB
Далее посчитайте страницы: 4 ГБ / 2 МБ = 2048. Добавьте запас (например, +5–10%), чтобы избежать ошибок старта при небольшой фрагментации, и выставьте в vm.nr_hugepages. После рестарта Postgres в журнале появится сообщение о использовании huge pages. Если страниц недостаточно, Postgres не стартует или вернётся к обычным страницам (при huge_pages=try), что легко упустить — держите on, когда уверены в расчёте, чтобы не получить «ложно зелёный» запуск.
MySQL/MariaDB: innodb_use_large_pages
В my.cnf включите:
[mysqld]
innodb_buffer_pool_size=8G
innodb_use_large_pages=ON
8 ГБ соответствуют 4096 страницам по 2 МБ. Зарезервируйте их через vm.nr_hugepages=4096 с запасом. Если страниц меньше, InnoDB либо частично уйдёт в обычные страницы, либо отрапортует в логах, что большие страницы недоступны. Следите, чтобы размер буфера был кратен 2 МБ.
THP: режим madvise vs never
Для веб-узлов с упором на PHP и кеши без строгих SLO по задержкам можно рассмотреть madvise. Так приложения, которые умеют просить THP, получат его, а ядро не будет агрессивно дефрагментировать память. Для баз данных и смешанных узлов с высокой нагрузкой по I/O и блокировкам — never.
Проверяйте также параметр дефрагментации THP — он должен быть в «never», иначе даже при madvise можно поймать синхронную дефрагментацию:
cat /sys/kernel/mm/transparent_hugepage/defrag
# ожидаем [never]
Виртуализация и VDS: есть ли подвох
На уровне гипервизора провайдер может использовать свои huge pages. Это прозрачно для гостя: внутри VDS вы настраиваете THP и явные HugePages независимо. Главное — адекватно оценивать доступную RAM. Резерв под hugetlb в госте «вырезает» память из общего пула, поэтому мониторинг свободной памяти и свопа обязателен.
Проверка и диагностика
- /proc/meminfo: следите за
HugePages_Total,HugePages_Free,HugePages_Rsvd. - Логи СУБД: при старте MySQL/MariaDB и Postgres обычно пишут, удалось ли задействовать huge pages.
- Метрики латентности: сравните p95/p99 до и после. Часто именно хвост задержек — главный выигрыш, а не среднее.
- Ошибки «Cannot allocate memory»: увеличьте
vm.nr_hugepagesили уменьшите размер буферов.
Частые ошибки и как их избегать
- Забыли отключить THP для баз данных. Симптомы: случайные подвисания, рост времени чекпоинтов/вакуума, нестабильный p99.
- Недорезервировали страницы: OPcache/JIT или InnoDB не смогли занять huge pages и молча откатились.
- Выделили слишком много: системе не хватает RAM под остальное, начинается давление памяти и swapping у других процессов.
- Некратные объёмы: буферы не кратны 2 МБ — часть уйдёт в обычные страницы, эффект размывается.
- Непоследовательность после перезагрузки: забытый unit/конфиг — и THP снова «always».
Мини-чеклист перед внедрением
- Зафиксируйте цель: снизить p95/p99 на PHP или стабилизировать СУБД.
- Отключите THP:
neverдля БД,madviseилиneverдля PHP-узлов с жёсткими SLO. - Рассчитайте и зарезервируйте
vm.nr_hugepagesс запасом. - Включите:
opcache.huge_code_pages=1,huge_pages=onв Postgres,innodb_use_large_pages=ONв MySQL/MariaDB. - Перезапустите сервисы, проверьте логи и метрики.
- Измерьте до/после и оставьте документацию в репозитории/вики проекта.
Итоги
THP — удобный, но непредсказуемый помощник, полезный разве что в режиме madvise на веб-узлах без жёстких SLO. Для СУБД — уверенное «нет». Настоящую пользу приносят явные HugePages: OPcache и JIT у PHP выигрывают стабильно, а InnoDB и PostgreSQL получают предсказуемую производительность без скачков из-за дефрагментации. Ключ к успеху — корректный расчёт, аккуратное резервирование страниц и дисциплина при сопровождении (контроль после перезагрузок, проверка логов и регулярные ревизии объёмов по мере роста данных).


