上篇:PostgreSQL 标准大页
1. 为什么要大页:页表会吃掉内存
Linux 默认内存页 4 KB。进程把 共享内存(PostgreSQL 的 shared_buffers 等)映射进自己的虚拟地址空间时,内核要为这些映射维护 页表(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。
标准大页(显式 Huge Pages):在 OS 预留 2 MB 或 1 GB 一页,同样 24GB 映射所需页表项数量大约按页大小成比例下降(相对 4KB 常差 两个数量级以上)。
2. 标准大页是什么(Hugetlbfs)
| 项 | 说明 |
|---|---|
| 机制 | 管理员通过 vm.nr_hugepages 等在启动前 预留 一块「大页池」 |
| 大小 | 常见 2 MB(Hugepagesize: 2048 kB);部分机器还有 1 GB |
| 分配特点 | 启动前预留、大小固定;不够则 PG huge_pages=on 会 启动失败 |
| PostgreSQL | 共享段尽量从 Hugetlb 分配;参数 huge_pages |
与「普通 4KB 页 + 内核自动合并」的 透明大页(THP) 不是同一套机制(见下篇)。
3. PostgreSQL 参数与容量计算
postgresql.conf
| 值 | 行为 |
|---|---|
try(常见默认) |
能用大页就用,不够则退回 4KB |
on |
必须用足大页,否则 拒绝启动 |
off |
不用大页 |
启动前查需要多少页(官方文档 18.4.5):
|
|
示例:shared_memory_size_in_huge_pages = 3170,Hugepagesize = 2048 kB → 约需 3170 个 2MB 大页(≈ 6.2GB 大页池,且要覆盖 整段共享内存,不只 shared_buffers 一项)。
OS 预留(2MB 页):
|
|
写入 /etc/sysctl.conf 持久化。若碎片导致一次分配失败,可 重试或重启后再设(重启后普通页释放,便于凑连续大页)。
可选:vm.hugetlb_shm_group、ulimit -l(lock mem)按环境配置。
1GB 大页:设置 huge_page_size = 1GB 时,用 hugepages-1048576kB 路径预留,并重算页数。
4. 验证
|
|
实例启动后,在日志或 shared_memory_size_in_huge_pages 与 HugePages_Free 变化上确认是否走大页。
下篇:透明大页 vs 标准大页
1. 对照表
| 标准大页(Hugetlb / 显式) | 透明大页 THP | |
|---|---|---|
| 谁分配 | 管理员 nr_hugepages 预留 |
内核 khugepaged 等 自动 把 4KB 合并为 2MB |
| 是否需改应用 | PG 设 huge_pages;OS 预留 |
应用 无感,默认可能已开 |
| 大小 | 2MB / 1GB 固定 | 多为 2MB 透明合并 |
| 不足时 | on → 启动失败;try → 4KB |
静默退回 4KB 或 延迟合并 |
| 可预测性 | 高(池子固定) | 低(合并时机、拆分会带来延迟抖动) |
| 数据库常见态度 | PG 推荐在 Linux 上用(try/on) | MySQL 官方/实践多建议关闭 |
2. 透明大页在做什么
THP 目标:让 普通程序 不修改代码也能减少 TLB miss、降低页表体积。
过程大致是:进程仍按 4KB 申请;内核在后台把 连续、合适 的 4KB 物理页 合并 成 2MB 大页。
对数据库不利点(MySQL 文档与社区反复提及):
- 合并/拆分与业务竞争 CPU,可能造成 延迟尖刺(p99 变差)。
- 内存紧凑方式不可控,与 大块预分配、长期驻留 的 Buffer Pool / shared_buffers 模型不匹配。
- 在 内存压力大 时拆大页,可能触发 额外 stall。
因此:不是「大页不好」,而是 「自动、不可控的 THP」不适合做低延迟数据库默认项。
3. 标准大页在做什么(再强调)
管理员先划好 固定大页池 → PostgreSQL 启动时 明确请求 Hugetlb → 映射从池里来,无 khugepaged 后台合并。
适合 PG 的原因:
- shared_buffers + 其它共享段 体积大、生命周期长、多进程重复映射(页表问题突出)。
- 容量可 事先算清(
shared_memory_size_in_huge_pages)。 - 行为可 复现,利于容量规划。
4. 关闭透明大页(Linux)
检查:
|
|
常见生产设置(重启后仍生效需写 udev/systemd/rc 或 grub,按发行版为准):
|
|
部分系统用 madvise 而非 never,数据库主机仍建议偏向 never,避免 InnoDB/PG 共享内存被意外 THP 影响。
MySQL:单进程与「关 THP、少谈标准大页」
1. 和 PostgreSQL 模型不同
| PostgreSQL | MySQL(InnoDB) | |
|---|---|---|
| 架构 | 一连接一进程,各映射 整段 shared_buffers | 单 mysqld 多线程,一份 地址空间映射 Buffer Pool |
| 页表压力 | 连接数 × 每进程页表(教材 500×48MB 场景) | 主要是 单进程 映射超大 Buffer Pool,不乘连接数 |
| 标准大页 | 收益大、文档完善(huge_pages + nr_hugepages) |
可用 Hugetlb 配 Buffer Pool,但运维重点往往是 先关 THP |
所以:不是 MySQL「不能用」标准大页,而是 页表被连接数放大 的典型痛点在 PG;MySQL 更常先处理 THP 带来的抖动。
2. MySQL 为何不推荐透明大页
Oracle / MySQL 8.0 性能文档与 Troubleshooting InnoDB 等均建议:在 Linux 上 禁用 THP,由 InnoDB 自己管理 Buffer Pool 页,而不是让内核异步合并。
实践检查:
|
|
3. MySQL 与标准大页(了解即可)
若坚持 Hugetlb:需在 OS 预留足够 2MB/1GB 大页,再让 InnoDB 在支持的路径 使用(与版本、分配方式有关)。容量规划要按 整池 innodb_buffer_pool_size 算,步骤类似 PG,但 社区默认叙事仍是:关 THP + 调好 Buffer Pool,除非有明确 TLB/页表压测数据再上标准大页。
不建议:在 MySQL 上 只开 THP、不关 never,却指望替代标准大页调优。
对照小结
| 主题 | PostgreSQL | MySQL |
|---|---|---|
| 页表放大 | 多进程 × 大 shared_buffers | 单进程大 Buffer Pool |
| 首选大页策略 | OS 预留 标准大页 + huge_pages=try/on |
关闭 THP |
| 透明大页 | 生产常 关 THP,与 PG 标准大页 并行 | 明确不建议 THP |
| 标准大页 | 案例主场景 | 可选,非入门必做 |
参考
- PostgreSQL:18.4.5 Linux Huge Pages
- Linux:
Documentation/admin-guide/mm/hugetlbpage.rst - MySQL 8.0:InnoDB Troubleshooting(Transparent huge pages 相关说明)