How to Configure Huge Pages for Database Performance on RHEL 7

Database engines — particularly Oracle, PostgreSQL, and MySQL/InnoDB — allocate and manage large memory pools for their buffer caches and shared memory segments. By default, the Linux kernel manages memory in 4 KB pages, which means that a 32 GB database buffer pool requires 8 million individual page table entries. Every memory access must be translated through these entries, and the Translation Lookaside Buffer (TLB), which caches recent translations, can only hold a few thousand entries. When the working set exceeds TLB capacity, TLB misses trigger expensive page table walks. Huge pages solve this by enlarging the granularity of memory management to 2 MB (or 1 GB with gigantic pages), reducing TLB pressure by a factor of 512 and eliminating the overhead of kernel memory management for those regions. This guide covers both Transparent Huge Pages and explicit huge page configuration on RHEL 7, along with per-database engine configuration.

Prerequisites

  • RHEL 7 system with root access
  • A database engine installed (PostgreSQL, MySQL, or Oracle) or planned for installation
  • Sufficient physical RAM — huge pages reserved but unused are wasted; size them to your workload
  • The tuned package installed: yum install -y tuned
  • GRUB 2 access for kernel parameter changes (Step 6)

Step 1: Understanding Transparent Huge Pages

RHEL 7 ships with Transparent Huge Pages (THP) enabled by default. THP is a kernel subsystem that automatically promotes 4 KB pages to 2 MB huge pages when a process allocates a large contiguous memory region. While THP improves throughput for some workloads, it is widely known to cause severe latency spikes in database environments due to the khugepaged daemon periodically scanning and collapsing pages in the background, stalling process execution unpredictably.

Check the current THP status:

cat /sys/kernel/mm/transparent_hugepage/enabled

The output shows the active setting in brackets:

[always] madvise never

A setting of [always] means THP is active for all memory allocations. The options are:

  • always — THP enabled for all processes
  • madvise — THP only for regions explicitly requesting it via madvise(MADV_HUGEPAGE)
  • never — THP completely disabled

Also check the defrag setting, which controls when the kernel compacts memory to create huge page-sized contiguous regions:

cat /sys/kernel/mm/transparent_hugepage/defrag

Step 2: Disabling THP for Database Workloads

The recommended approach for production database servers on RHEL 7 is to disable THP entirely and use explicit (static) huge pages instead. There are three methods to disable THP at boot.

Method A: Using a tuned Profile (Recommended)

The tuned daemon manages system tuning profiles and handles THP via the transparent_hugepages setting. Create a custom profile inheriting from throughput-performance:

mkdir -p /etc/tuned/database-nothp
vi /etc/tuned/database-nothp/tuned.conf
[main]
summary=Database profile with THP disabled
include=throughput-performance

[vm]
transparent_hugepages=never
tuned-adm profile database-nothp
tuned-adm active

Method B: systemd Service to Disable THP at Boot

Create a one-shot systemd service that disables THP early in the boot process:

vi /etc/systemd/system/disable-thp.service
[Unit]
Description=Disable Transparent Huge Pages
DefaultDependencies=no
After=sysinit.target local-fs.target
Before=basic.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'
RemainAfterExit=yes

[Install]
WantedBy=basic.target
systemctl daemon-reload
systemctl enable disable-thp --now
systemctl status disable-thp

Method C: Kernel Boot Parameter

Add transparent_hugepage=never to the kernel command line in GRUB:

vi /etc/default/grub

Append to the GRUB_CMDLINE_LINUX line:

GRUB_CMDLINE_LINUX="... transparent_hugepage=never"
grub2-mkconfig -o /boot/grub2/grub.cfg

Verify after reboot:

cat /sys/kernel/mm/transparent_hugepage/enabled
# always madvise [never]

Step 3: Configuring Explicit (Static) Huge Pages

Static huge pages are pre-allocated at boot and held in a reserved pool. Processes must explicitly request them via mmap(MAP_HUGETLB) or shared memory with SHM_HUGETLB. Because they are pre-allocated, they are never swapped out and are always available to the database engine.

First, determine how many 2 MB huge pages your database needs. For a PostgreSQL shared_buffers of 16 GB:

# 16 GB / 2 MB = 8192 huge pages
# Add ~5% overhead
# Total: ~8600

Set the number of huge pages via sysctl:

sysctl -w vm.nr_hugepages=8600

Make it persistent:

echo 'vm.nr_hugepages = 8600' >> /etc/sysctl.d/99-hugepages.conf
sysctl -p /etc/sysctl.d/99-hugepages.conf

For NUMA systems, allocate huge pages per NUMA node to avoid cross-node memory access:

# Check NUMA topology
numactl --hardware

# Allocate per node
echo 4300 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 4300 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

Step 4: Verifying Huge Page Allocation

After setting vm.nr_hugepages, verify the allocation succeeded by checking /proc/meminfo:

grep -i huge /proc/meminfo

Sample output:

AnonHugePages:    524288 kB
HugePages_Total:    8600
HugePages_Free:     8600
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
  • HugePages_Total — requested huge pages allocated
  • HugePages_Free — unused huge pages (should decrease as the database starts)
  • HugePages_Rsvd — reserved but not yet faulted in by the process
  • HugePages_Surp — surplus pages (allocated beyond the pool limit; usually 0)

If HugePages_Total is less than requested, physical memory fragmentation prevented allocation. Reboot soon after setting vm.nr_hugepages for best results, before memory becomes fragmented.

Step 5: Configuring PostgreSQL to Use Huge Pages

PostgreSQL 9.4+ on Linux supports huge pages via the huge_pages parameter:

vi /var/lib/pgsql/data/postgresql.conf
# Enable huge pages (try = use if available, on = require, off = disable)
huge_pages = on

# Set shared_buffers to match your huge page allocation
shared_buffers = 16GB

The PostgreSQL process user (postgres) must be allowed to lock the huge pages in memory. Add to /etc/security/limits.conf:

postgres  soft  memlock  unlimited
postgres  hard  memlock  unlimited
systemctl restart postgresql
psql -U postgres -c "SHOW huge_pages;"

Step 6: Configuring MySQL/InnoDB to Use Huge Pages

MySQL supports huge pages through the large-pages option:

vi /etc/my.cnf
[mysqld]
large-pages
innodb_buffer_pool_size = 16G

The mysql user needs memory lock permissions:

mysql  soft  memlock  unlimited
mysql  hard  memlock  unlimited
systemctl restart mysqld
mysql -u root -p -e "SHOW VARIABLES LIKE 'large_pages';"

Step 7: Adding Huge Page Parameters to the GRUB Kernel Command Line

For very large allocations or gigantic 1 GB pages, pass parameters directly to the kernel at boot:

vi /etc/default/grub
# Allocate 8600 standard 2 MB huge pages at boot
GRUB_CMDLINE_LINUX="... hugepages=8600"

# For 1 GB pages (requires CPU support — check /proc/cpuinfo for pdpe1gb flag)
GRUB_CMDLINE_LINUX="... hugepagesz=1G hugepages=16 default_hugepagesz=1G"
grub2-mkconfig -o /boot/grub2/grub.cfg

Check CPU support for 1 GB pages:

grep pdpe1gb /proc/cpuinfo | head -1

Conclusion

Huge page configuration is one of the highest-impact tuning tasks available for database servers on RHEL 7. Disabling Transparent Huge Pages eliminates unpredictable latency spikes caused by background memory compaction, while pre-allocating static huge pages through vm.nr_hugepages provides the database engine with a dedicated, locked, never-swapped memory pool. Whether you are running PostgreSQL, MySQL, or Oracle, the combination of THP disabled and explicit huge pages enabled can reduce database query latency by 10–30% on memory-intensive workloads — a significant improvement achievable entirely through configuration, with no application code changes required.