mysql官方建议生产环境关闭大页内存配置,因显式hugepages为实验性功能且未深度适配innodb内存管理,而透明大页(thp)的动态合并机制会干扰innodb内存分配、引发延迟毛刺和cpu开销;真正应优先优化tlb压力而非盲目开启大页。

直接配,但必须按顺序做对四件事:关透明大页、预分配显式大页、放开 mysql 用户锁内存权限、my.cnf 里写对 large-pages。漏一步,@@large_pages 就是 1,但实际没用上大页。
为什么 MySQL 启动后 @@large_pages 显示 1 却没生效
这是最常踩的坑——MySQL 看到配置就设全局变量为 1,但真正申请大页失败时完全不报错,静默回退到 4KB 页。根本原因通常是:
-
vm.nr_hugepages没提前分配,或值小于innodb_buffer_pool_size所需页数(2MB 页下:页数 = ceil(缓冲池字节数 / 2097152)) -
/etc/security/limits.conf漏了mysql soft memlock unlimited和mysql hard memlock unlimited - mysqld 由 systemd 启动,但
/usr/lib/systemd/system/mysqld.service里没加LimitMEMLOCK=infinity,systemd 会覆盖 limits.conf 设置 - MySQL 进程不是以 root 或带
CAP_IPC_LOCK权限的用户启动(普通 mysql 用户默认无此能力)
如何确认系统支持且已禁用 Transparent HugePages
显式 HugePages 和 THP 互斥,THP 不仅对 MySQL 无效,还会干扰 InnoDB 内存分配节奏,引发延迟抖动。必须关掉:
- 检查状态:
cat /sys/kernel/mm/transparent_hugepage/enabled,输出含[always]或[madvise]就得处理 - 临时关(重启失效):
echo never > /sys/kernel/mm/transparent_hugepage/enabled - 永久关(生产必备):在
/etc/default/grub的GRUB_CMDLINE_LINUX行末尾加transparent_hugepage=never,再运行sudo update-grub && sudo reboot - 验证是否真关:
cat /proc/meminfo | grep AnonHugePages必须返回AnonHugePages: 0 kB
计算和分配 vm.nr_hugepages 的关键细节
别直接套公式,得看 innodb_buffer_pool_size 实际值和页大小。常见错误是忽略“向上取整”和“内核保留开销”:
- 先查当前页大小:
grep Hugepagesize /proc/meminfo,多数系统是2048 kB(即 2MB) - 假设
innodb_buffer_pool_size = 24G,换算:24 × 1024 × 1024 × 1024 = 25769803776 字节 - 所需页数 = ceil(25769803776 / 2097152) = 12288,不是 12287.99 —— 少 1 页,最后那点内存就走普通页
- 生产建议多留 5% 缓冲(防其他内存结构占用),所以设
vm.nr_hugepages = 12902 - 写入
/etc/sysctl.conf:vm.nr_hugepages = 12902,再执行sudo sysctl -p - 验证:
grep HugePages_Total /proc/meminfo应等于你设的值
验证大页是否真被 MySQL 使用
光看 SELECT @@large_pages; 是不够的,得交叉验证两个地方:
- 查 InnoDB 状态:
SHOW ENGINE INNODB STATUS\G,搜Large pages used,出现类似Large pages used: 12288 (25165824 KB)才算实锤 - 查进程内存映射:
sudo pmap -x $(pgrep mysqld) | grep "huge",应有若干行显示2097152K大小的huge标记段 - 观察
/proc/meminfo中HugePages_Free是否明显小于HugePages_Total(比如只差几十页),说明已被占用 - 注意:如果
HugePages_Surp > 0,说明内核动态分配了超出预设的页,这通常意味着配置值偏低或存在碎片
最容易被忽略的是 systemd 对 ulimit 的覆盖,以及 large-pages 配置项不能加等号、不能加引号、不能放在 [client] 或其他段——它只在 [mysqld] 段下、单独一行、无任何符号才有效。











