Linux gestiona la memoria en páginas de 4 KB por defecto. Una base de datos con 16 GB de shared_buffers necesita más de cuatro millones de esas páginas, y cada acceso a memoria depende de que su traducción esté en la TLB de la CPU, que es pequeña. Las huge pages de 2 MB reducen el número de entradas 512 veces, lo que baja los fallos de TLB y el tamaño de las tablas de páginas de cada proceso. En este tutorial reservarás huge pages en Ubuntu 24.04, configurarás PostgreSQL 16 (y opcionalmente MySQL 8.0) para usarlas y verificarás que la memoria compartida está realmente en huge pages.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS o un servidor dedicado de CubePath, con un usuario no root con privilegios
sudo. - PostgreSQL 16 instalado desde los repositorios de Ubuntu (
sudo apt install postgresql) o MySQL 8.0 (sudo apt install mysql-server). - Memoria suficiente: la ganancia se nota con buffers de varios GB. Con menos de 4 GB de RAM el beneficio es muy pequeño.
ImportanteLa memoria reservada como huge pages deja de estar disponible para el resto del sistema, aunque la base de datos no la use. Reserva solo lo que la base de datos necesita.
Paso 1: Revisar el estado actual de la memoria
Comprueba el tamaño de huge page por defecto y cuántas hay reservadas:
grep -i huge /proc/meminfo
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
FileHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
Hugepagesize: 2048 kB indica páginas de 2 MB, que es lo que usarás en esta guía. HugePages_Total: 0 significa que aún no hay ninguna reservada.
Mira también el modo de las Transparent Huge Pages (THP), que son un mecanismo distinto que el kernel aplica de forma automática:
cat /sys/kernel/mm/transparent_hugepage/enabled
always [madvise] never
Ubuntu usa madvise por defecto: el kernel solo usa THP en la memoria que la aplicación marca expresamente. Es el valor adecuado para un servidor de bases de datos, así que no lo cambies. Las huge pages que vas a configurar aquí son explícitas (hugetlbfs), se reservan por adelantado y no se ven afectadas por este ajuste.
Paso 2: Calcular cuántas huge pages necesita PostgreSQL
PostgreSQL coloca en huge pages su segmento principal de memoria compartida, cuyo tamaño depende sobre todo de shared_buffers. Un punto de partida habitual es el 25 % de la RAM. Por ejemplo, en un servidor con 16 GB:
sudo -u postgres psql -c "ALTER SYSTEM SET shared_buffers = '4GB';"
Reinicia PostgreSQL para que el nuevo tamaño se aplique:
sudo systemctl restart postgresql
Desde PostgreSQL 15 el propio servidor calcula cuántas huge pages necesita para su memoria compartida:
sudo -u postgres psql -c "SHOW shared_memory_size_in_huge_pages;"
shared_memory_size_in_huge_pages
----------------------------------
2138
(1 row)
Apunta este número. Reserva un pequeño margen por encima (un 2 o 3 %) para no tener que recalcular si cambias algún parámetro menor. En el ejemplo, 2200 páginas de 2 MB son unos 4,3 GB.
Paso 3: Reservar las huge pages en el kernel
Crea un archivo de sysctl propio para que la reserva se mantenga tras un reinicio:
sudo nano /etc/sysctl.d/60-hugepages.conf
Añade la línea con el número de páginas calculado:
vm.nr_hugepages = 2200
Aplica la configuración:
sudo sysctl -p /etc/sysctl.d/60-hugepages.conf
vm.nr_hugepages = 2200
Comprueba que el kernel ha podido reservarlas todas:
grep -E 'HugePages_(Total|Free)' /proc/meminfo
HugePages_Total: 2200
HugePages_Free: 2200
Si HugePages_Total es menor que el valor pedido, la memoria está fragmentada y el kernel no encuentra bloques contiguos de 2 MB. Consulta la sección de solución de problemas.
Paso 4: Obligar a PostgreSQL a usar huge pages
El valor por defecto de huge_pages es try: PostgreSQL intenta usarlas y, si no puede, arranca en silencio con páginas normales. Ponlo en on para que falle al arrancar en lugar de degradar el rendimiento sin avisar:
sudo -u postgres psql -c "ALTER SYSTEM SET huge_pages = 'on';"
Reinicia el servicio:
sudo systemctl restart postgresql
Comprueba que el clúster está en marcha:
sudo systemctl status postgresql@16-main --no-pager
● [email protected] - PostgreSQL Cluster 16-main
Loaded: loaded (/usr/lib/systemd/system/[email protected]; enabled-runtime; preset: enabled)
Active: active (running) since ...
Si el servicio no arranca, revisa el log. El error típico cuando faltan páginas es este:
sudo tail -n 20 /var/log/postgresql/postgresql-16-main.log
FATAL: could not map anonymous shared memory: Cannot allocate memory
HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages.
En ese caso aumenta vm.nr_hugepages en /etc/sysctl.d/60-hugepages.conf, vuelve a aplicarlo y reinicia PostgreSQL.
Paso 5: Verificar que la memoria compartida está en huge pages
Con PostgreSQL arrancado, vuelve a mirar los contadores:
grep -E 'HugePages_(Total|Free|Rsvd)' /proc/meminfo
HugePages_Total: 2200
HugePages_Free: 2105
HugePages_Rsvd: 2043
Estos números confirman que funciona:
HugePages_Rsvdpasa de 0 a casi el valor calculado en el paso 2: PostgreSQL ha reservado sus páginas.HugePages_Freebaja a medida que los backends tocanshared_buffers. Tras un rato de carga real,FreemenosRsvdqueda cerca del margen que dejaste.
Si HugePages_Rsvd sigue en 0, PostgreSQL no está usando huge pages: comprueba que SHOW huge_pages; devuelve on y que reiniciaste el servicio.
Paso 6 (opcional): Configurar MySQL 8.0 con huge pages
MySQL usa huge pages para el buffer pool de InnoDB a través de memoria compartida System V. Para eso el usuario mysql tiene que pertenecer al grupo autorizado por el kernel en vm.hugetlb_shm_group.
Calcula las páginas a partir de innodb_buffer_pool_size más un 10 % de margen. Con un buffer pool de 4 GB: 4096 MB / 2 MB = 2048 páginas, más el margen, unas 2250. Si PostgreSQL y MySQL comparten servidor, suma ambas cifras en vm.nr_hugepages.
Obtén el GID del usuario mysql:
id -g mysql
110
Edita el archivo del paso 3:
sudo nano /etc/sysctl.d/60-hugepages.conf
Deja el número de páginas y el grupo, usando el GID que obtuviste:
vm.nr_hugepages = 2250
vm.hugetlb_shm_group = 110
Aplica los cambios:
sudo sysctl -p /etc/sysctl.d/60-hugepages.conf
La documentación de MySQL recomienda además quitar el límite de memoria bloqueada del proceso. Crea un override de systemd:
sudo systemctl edit mysql
Añade estas líneas en la zona editable del archivo:
[Service]
LimitMEMLOCK=infinity
Activa las huge pages en la configuración de MySQL:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
Añade estas líneas dentro de la sección [mysqld]:
innodb_buffer_pool_size = 4G
large_pages = ON
Reinicia MySQL y comprueba la variable:
sudo systemctl restart mysql
sudo mysql -e "SHOW VARIABLES LIKE 'large_pages';"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| large_pages | ON |
+---------------+-------+
La variable solo refleja la configuración. Para confirmar que la asignación ha funcionado, revisa que el log de errores no contiene avisos de HugeTLB y que HugePages_Rsvd o HugePages_Free han cambiado en /proc/meminfo:
sudo grep -i hugetlb /var/log/mysql/error.log
Si no devuelve nada y los contadores se han movido, InnoDB está usando huge pages. Si ves Failed to allocate, faltan páginas o el GID de vm.hugetlb_shm_group no es el del usuario mysql.
Paso 7 (opcional): Usar huge pages de 1 GB
En servidores con mucha RAM (por ejemplo, shared_buffers de 64 GB o más), las páginas de 1 GB reducen todavía más la presión sobre la TLB. Primero comprueba que la CPU las admite:
grep -m1 -o pdpe1gb /proc/cpuinfo
pdpe1gb
Si no aparece nada, la CPU (o la CPU virtual que expone el hipervisor) no las soporta y debes quedarte con páginas de 2 MB. Las páginas de 1 GB se deben reservar al arrancar, cuando la memoria aún no está fragmentada. Edita GRUB:
sudo nano /etc/default/grub
Añade los parámetros a la línea existente, ajustando el número de páginas a tu caso:
GRUB_CMDLINE_LINUX_DEFAULT="hugepagesz=1G hugepages=16"
Si esa línea ya tenía otros valores (por ejemplo quiet splash), mantenlos y añade los nuevos separados por un espacio. Regenera la configuración de GRUB y reinicia:
sudo update-grub
sudo reboot
Tras el reinicio, comprueba la reserva en el directorio del tamaño de 1 GB:
cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
16
Por último, indica a PostgreSQL ese tamaño y reinícialo:
sudo -u postgres psql -c "ALTER SYSTEM SET huge_page_size = '1GB';"
sudo systemctl restart postgresql
Con páginas de 1 GB, elimina o reduce vm.nr_hugepages en /etc/sysctl.d/60-hugepages.conf para no reservar también páginas de 2 MB que nadie va a usar.
Solución de problemas
HugePages_Totalse queda por debajo de lo pedido. La memoria está fragmentada. Pide al kernel que compacte la memoria conecho 1 | sudo tee /proc/sys/vm/compact_memoryy vuelve a aplicar el sysctl. Si sigue sin llegar, reinicia: el archivo de/etc/sysctl.d/se aplica al principio del arranque, cuando hay bloques libres de sobra.- El sistema se queda sin memoria tras reservar las páginas. Has reservado demasiadas. Las huge pages no se pueden usar para procesos normales ni para la caché de disco. Baja
vm.nr_hugepages, deja al menos un 20-25 % de la RAM fuera de la reserva, porque PostgreSQL también depende de la caché de disco del sistema. - PostgreSQL arranca pero
HugePages_Rsvdes 0.huge_pagessigue entryo el cambio no se aplicó. EjecutaSHOW huge_pages;y reinicia el servicio (no basta con recargarlo). - Has cambiado
shared_buffers. Repite el paso 2: el número de páginas necesario cambia con él.
Conclusión
Has reservado huge pages en Ubuntu 24.04, has calculado la cantidad exacta a partir de shared_memory_size_in_huge_pages y has configurado PostgreSQL para que no arranque sin ellas, con MySQL como alternativa. Para medir el efecto, ejecuta una carga representativa con pgbench antes y después del cambio, vigila HugePages_Free en tu sistema de monitorización (node_exporter publica node_memory_HugePages_Free) y revisa el cálculo cada vez que cambies el tamaño de los buffers.
