Linux 上谈「大页」时,实际涉及 两套互不相同的机制

  • 标准大页(Explicit Huge Pages / Hugetlbfs):管理员预留、应用显式申请。
  • 透明大页(Transparent Huge Pages,THP):内核在后台自动把 4 KB 页合并为 2 MB 页。

PostgreSQL 与 MySQL 的文档和最佳实践都围绕这两套机制展开,但适用场景不同。下文按 标准大页 → 透明大页 → 两者对比 → MySQL 适用 的顺序说明。


一、PostgreSQL 标准大页(Hugetlbfs)

1.1 为什么 PostgreSQL 特别需要标准大页

Linux 默认内存页大小为 4 KB。PostgreSQL 把 shared_buffers 以及 WAL、锁表等组件放在 共享内存段 里;每个 backend 进程启动时,都要把这段共享内存 映射进自己的虚拟地址空间

映射越大,内核要维护的 页表(Page Table) 越多。粗算(64 位、4 KB 页,教材常用 每页约 8 字节 页表开销):

  • 单进程映射 24 GB shared_buffers
    (24G / 4K) × 8B ≈ 48 MB 页表/进程
  • 500 个 backend(一连接一进程):
    500 × 48 MB ≈ 24 GB 页表(量级上可与 shared_buffers 相当)

页表不是连接瞬间一次性占满,而是访问共享页时 逐步分配;跑久了仍可能涨到很大,表现为 MemAvailable 莫名下降、甚至 OOM

标准大页把单页从 4 KB 提升到 2 MB1 GB,同样 24 GB 映射所需页表项数量大约按页大小成比例下降(相对 4 KB 常差 两个数量级以上)。这是 PostgreSQL 在 Linux 上 优先推荐标准大页 的根本原因:多进程 × 大共享段 会把页表压力放大到连接数级别。

1.2 标准大页是什么

说明
内核机制 Hugetlbfs,通过 vm.nr_hugepages 等在启动前 预留 固定大小的「大页池」
常见页大小 2 MBHugepagesize: 2048 kB);部分机器支持 1 GB
分配特点 先预留、后使用;池子大小固定,预留不足时 PostgreSQL 可能 启动失败(取决于 huge_pages 参数)
PostgreSQL 侧 共享内存段尽量从 Hugetlb 池分配;由参数 huge_pages 控制

标准大页 不是 内核在运行时自动合并出来的,而是 OS 管理员 + 数据库实例启动时 共同完成的显式分配。

1.3 PostgreSQL 参数

postgresql.conf 中的 huge_pages

行为
try(常见默认) 能用大页就用,不够则退回 4 KB
on 必须用足大页,否则 拒绝启动
off 不使用大页

huge_page_size(可选):指定 2MB1GB,需与 OS 预留的大页尺寸一致。

1.4 容量计算与 OS 预留

PostgreSQL 官方文档 18.4.5 Linux Huge Pages 要求:在实例启动 之前 算清需要多少大页。

1)查 PostgreSQL 需要的大页个数

1
2
postgres -D $PGDATA -C shared_memory_size_in_huge_pages
grep ^Hugepagesize /proc/meminfo

示例:shared_memory_size_in_huge_pages = 3170Hugepagesize = 2048 kB → 需要 3170 个 2 MB 大页(约 6.2 GB 大页池)。

注意:这个数字覆盖 整段 PostgreSQL 共享内存,不只 shared_buffers,还包括 WAL buffers、锁空间等。

2)在 OS 上预留 2 MB 大页

1
2
3
4
5
# 临时生效
sysctl -w vm.nr_hugepages=3170

# 或按尺寸写 sysfs
echo 3170 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

写入 /etc/sysctl.conf 可持久化。若因内存碎片一次分配失败,可 重试或在维护窗口重启后再设(重启后普通页释放,更容易凑出连续大页)。

3)1 GB 大页

设置 huge_page_size = 1GB 时,改用 hugepages-1048576kB 路径预留,并按 1 GB 重算页数。

4)其他常见配置

  • vm.hugetlb_shm_group:允许指定组使用 Hugetlb 共享内存(按环境需要)。
  • ulimit -l(locked memory):部分环境需放宽,避免 mmap 大页失败。

1.5 验证是否生效

1
2
3
4
5
# 大页池总量与空闲
grep -E 'HugePages_Total|HugePages_Free|Hugepagesize' /proc/meminfo

# 当前预留数
cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

实例启动后对比 HugePages_Free 是否下降,并结合 shared_memory_size_in_huge_pages 与 PostgreSQL 日志确认共享段是否走大页。


二、透明大页(Transparent Huge Pages,THP)

2.1 透明大页是什么

透明大页(THP) 是 Linux 内核提供的 另一套 大页机制,目标对象是 普通应用程序:不改代码、不预留池子,也能在运行时获得 2 MB 大页带来的 TLB 收益。

与标准大页的关键不同:THP 对应用是「透明」的——进程仍按 4 KB 申请虚拟内存;内核在后台(主要是 khugepaged 等)扫描物理内存,把 连续、合适 的 4 KB 页 合并 成 2 MB 大页。内存紧张时,内核也可能 把大页拆回 4 KB

2.2 THP 的工作方式(简化)

1
2
3
4
5
6
7
应用 malloc/mmap → 仍按 4 KB 粒度映射
内核后台 khugepaged 扫描
连续 4 KB 物理页满足条件 → 合并为 2 MB THP
内存压力或 defrag 策略触发 → 可能拆回 4 KB

THP 的 合并时机、拆分时机 由内核策略决定,应用 无法精确控制

2.3 查看与配置 THP

1
2
3
# 当前策略
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag

enabled 常见取值:

含义
[always] 积极为匿名/文件映射创建 THP
[madvise] 仅对 MADV_HUGEPAGE 标记的区域尝试 THP
[never] 不创建 THP

defrag 控制合并/整理策略(如 alwaysdefermadvisenever),同样影响 何时、是否 在后台做页合并。

许多发行版默认 开启或半开启 THP,数据库主机上需要 主动确认,不能假设已是 never

2.4 生产环境为何常关闭 THP

THP 的设计目标是 通用 workload 的自动化优化,不是为 大块预分配、长期驻留、低延迟 的数据库共享内存模型量身定制。常见问题包括:

  1. 后台合并/拆分与业务争用 CPU,可能造成 延迟尖刺(p99 变差)。
  2. 合并时机不可预测,与 Buffer Pool / shared_buffers 这类 启动时一次性分配、长期不变 的内存模式不匹配。
  3. 内存压力下拆大页,可能触发 额外 stall,在高压下表现为偶发慢查询或 checkpoint 抖动。

因此:不是「大页本身不好」,而是 「自动、不可控的 THP」 不适合作为数据库的默认大页方案。这一点在 MySQL 官方文档中表述尤其明确;PostgreSQL 侧也通常 在启用标准大页的同时关闭 THP,避免两套机制互相干扰。

2.5 关闭透明大页(Linux)

1
2
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

重启后仍生效需写入 udev / systemd / grub 等启动脚本,具体方式因发行版而异。数据库专用主机建议偏向 never,而不是依赖 madvise「碰运气」。


三、标准大页与透明大页:区别与不同

两者名字都带「大页」,但 内核路径、控制方式、数据库适配度 完全不同。对照如下:

维度 标准大页(Hugetlb / 显式) 透明大页(THP)
本质 独立的大页池(Hugetlbfs) 在普通 4 KB 页之上的 自动合并
谁负责 管理员 预留 nr_hugepages应用 显式请求 内核 khugepaged 等后台线程
应用是否感知 需要(PostgreSQL 设 huge_pages 无感,默认可能已开
页大小 2 MB / 1 GB,固定 多为 2 MB 合并页
容量规划 可事先用 shared_memory_size_in_huge_pages 算清 不可精确规划,随运行时合并变化
不足时行为 huge_pages=on → 启动失败;try → 退回 4 KB 静默保持 4 KB 或 延迟合并
运行时行为 khugepaged 式后台合并/拆分 可能 异步合并、压力拆页 → 延迟抖动
可预测性 (池子固定、行为可复现) (合并时机依赖负载与内核策略)
与数据库共享内存 PostgreSQL 官方推荐在 Linux 尝试/启用 PostgreSQL / MySQL 生产实践 多建议关闭

一句话区分:

  • 标准大页 = 「先圈地,再入住」——管理员划好固定池子,PostgreSQL 启动时明确入住。
  • 透明大页 = 「先住 4 KB 单间,保洁员半夜帮你并成套房」——合并时机你说了不算。

能否互相替代?

不能。在 PostgreSQL 上 不能 用「开着 THP、不设 Hugetlb」来替代 huge_pages + nr_hugepages:THP 解决不了 500 个进程各自维护超大页表 的核心问题,还可能引入延迟抖动。正确做法是:PG 侧用标准大页解决页表与 TLB;同时关 THP 避免干扰


四、MySQL 中的体现与适用

MySQL(InnoDB)与 PostgreSQL 的进程模型不同,因此 同一套大页机制,收益与优先级也不同

4.1 架构差异:页表压力从哪来

PostgreSQL MySQL(InnoDB)
架构 一连接一进程,各映射 整段 shared_buffers 单 mysqld 多线程一份 地址空间映射 Buffer Pool
页表压力 连接数 × 每进程页表(上文 500×48 MB 场景) 主要是 单进程 映射超大 Buffer Pool,不随连接数倍增
大页首要矛盾 页表 + TLB(多进程重复映射) THP 带来的延迟抖动(其次才是单进程大池的 TLB)

因此:标准大页对 PostgreSQL 往往是「值得规划的容量项」;对 MySQL 则 「先关 THP」几乎是标配,「上标准大页」是进阶可选项

4.2 透明大页在 MySQL 中的体现

MySQL 8.0 InnoDB Troubleshooting 明确建议:在 Linux 上 禁用透明大页,由 InnoDB 自己管理 Buffer Pool 中的页,而不是让内核异步合并。

原因归纳:

  • InnoDB Buffer Pool 在启动时 预分配大块内存 并长期驻留,与 THP 延迟合并 模型冲突。
  • khugepaged 的合并/拆分会与 刷脏、checkpoint、IO 线程 竞争 CPU,表现为 吞吐或延迟不稳定
  • 部分版本启动时会对 THP 配置 打印警告,提示可能影响性能。

运维检查:

1
2
3
4
5
6
# THP 是否已关
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag

# 运行中 mysqld 是否映射了 AnonHugePages(若 THP 仍开,可能看到非零)
grep -iE 'AnonHuge|Huge' /proc/$(pidof mysqld)/smaps 2>/dev/null | head

MySQL 对 THP 的适用结论: 生产环境 关闭(never);不要指望 THP 替代任何 Buffer Pool 调优。

4.3 标准大页在 MySQL 中的体现

MySQL 可以 在 Linux 上配合 Hugetlb 使用标准大页,让 InnoDB Buffer Pool 尽量从 2 MB / 1 GB 大页池 分配,以降低 单进程 映射超大池时的 TLB 压力。但:

  • 社区与官方 默认叙事 仍是:关 THP + 调好 innodb_buffer_pool_size 与实例内存
  • 启用标准大页需要 OS 预留足够大页(按 innodb_buffer_pool_size 及其实际分配路径估算,步骤类似 PostgreSQL,但无 shared_memory_size_in_huge_pages 这样的一站式 GUC)。
  • 是否与你的 MySQL 版本、分配方式(mmap / shm 等)完全匹配,需 查对应版本说明并压测验证

MySQL 对标准大页的适用结论:

场景 建议
通用生产 关 THP;Buffer Pool 设为物理内存合理比例
超大 Buffer Pool(如数百 GB)且 TLB miss 明显 可评估 Hugetlb 标准大页,需 OS 预留 + 压测
仅开 THP、不做标准大页 不推荐

不建议的做法: 在 MySQL 上 保持 THP 为 always,却指望它替代标准大页或 Buffer Pool 调优——这是两套机制里 最不可控 的一种组合。

4.4 PostgreSQL 与 MySQL 对照小结

主题 PostgreSQL MySQL
页表放大来源 多进程 × 大 shared_buffers 单进程大 Buffer Pool
标准大页 推荐huge_pages=try/on + nr_hugepages 可选(超大池、有压测数据时)
透明大页 与标准大页 并行存在但应关闭 明确不建议
运维优先级 算清大页池 → 启 PG 标准大页 → 关 THP 先关 THP → 调 Buffer Pool → 再考虑标准大页

参考

相关:PG 与 MySQL 执行层内存对照 · InnoDB 进程内存总图