HugePage:为数据库启用大页支持
Module:
Categories:
内存大页的优缺点
对于数据库来说,启用大页有好处,也有缺点。
- OLAP 场景下的显著性能收益:大数据量扫描与批量计算
- 更可控的内存分配模型:启动时“锁定”需要的内存
- 提升内存访问效率,减少 TLB miss
- 降低内核页表维护开销
但也伴随着一些缺点:
- 额外的配置与维护复杂度
- 大页内存被锁定,对系统整体资源弹性要求高的环境来说缺乏灵活性
- 小规模内存场景收益有限,甚至会适得其反
请注意,HugePage 和 Transparent HugePage (透明大页)是两个不同的概念, Pigsty 会强制关闭 Transparent HugePage 以遵循数据库最佳实践。
什么时候启用大页?
如果你的场景满足以下条件,我们建议启用大页:
- OLAP 分析场景
- 超过 几十GB 的内存
- PostgreSQL 15+
- Linux 内核版本 > 3.10 (> EL7, > Ubuntu 16)
Pigsty 默认不启用大页,但你可以通过简单的配置启用,并配置为 PostgreSQL 专属的内存。
分配节点大页
要为节点启用大页面,用户可以使用以下两个参数:
node_hugepage_count
:精确指定要分配的 2MB 内存大页数量node_hugepage_ratio
:指定一个百分比,将内存的一部分以大页形式分配
这两个参数二选一,你可以直接指定要分配的(2MB)大页数量,或指定分配为大页的内存比例(0.00 - 0.90 ),前者具有更高优先级。
node_hugepage_count: 0 # 精确指定 2MB 大页面数量,优先级要高于 node_hugepage_ratio
node_hugepage_ratio: 0 # 分配为 2MB 大页面的内存比例,优先级要低于 node_hugepage_count
应用生效:
./node.yml -t node_tune
本质上是在:/etc/sysctl.d/hugepage.conf
中写入了 vm.nr_hugepages
参数值并执行了 sysctl -p
应用生效。
./node.yml -t node_tune -e node_hugepage_count=3000 # 精确分配 5000 个 2MB 大页(10GB)
./node.yml -t node_tune -e node_hugepage_ratio=0.30 # 以大页形式分配 30% 的内存
请注意,以上参数只是为节点启用大页,不仅仅是 PostgreSQL 可以使用。
PostgreSQL 服务器默认会在启动时尝试使用大页,如果系统中可用的大页数量不足,PostgreSQL 会继续使用普通页面启动。
如果你尝试降低大页数量,只有未被使用与保留的大页(Free)会被释放,已经被使用的大页会在进程退出后释放。
Pigsty 最多允许分配 90% 的内存作为大页,但对于 PostgreSQL 数据库来说,合理的范围通常在 25% - 40% 的内存。
建议用户设置:node_hugepage_ratio=0.30
,并在 PostgreSQL 启动后按需进一步调整大页数量。
查看大页状态
最直观的查看方法是使用 Pigsty 监控系统,这里给出了调整大页时的一个监控图表样例:
- Node Instance - Memory - HugePages Allocation
- 默认状态
- 启用大页,未使用
- 重启 PG ,使用/保留了一部分大页
- 进一步使用 PG,使用了更多大页
- 缩减大页数量,回收未使用的大页
- 重启 PG,彻底释放保留的大页
你可以直接 cat /proc/meminfo | grep Huge
查看大页状态。
$ cat /proc/meminfo | grep Huge
默认情况下,没有启用大页面,大页面数量(Total)为 0:
AnonHugePages: 8192 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
启用了大页面,总共有 6015 个大页面,全部空闲可用:
AnonHugePages: 8192 kB
ShmemHugePages: 0 kB
FileHugePages: 0 kB
HugePages_Total: 6015
HugePages_Free: 6015
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 12318720 kB
如果这时候重启 PostgreSQL (默认会尝试使用大页)
sudo su - postgres
pg-restart
那么 PostgreSQL 会使用 保留预定 (Rsvd,Reserved)所需的大页,用于共享缓冲区,例如这里保留了 5040 个。
AnonHugePages: 8192 kB
ShmemHugePages: 0 kB
FileHugePages: 0 kB
HugePages_Total: 6015
HugePages_Free: 5887
HugePages_Rsvd: 5040
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 12318720 kB
如果我们给 PostgreSQL 增加一些负载,比如 pgbench -is10
,那么 PostgreSQL 会开始使用更多大页(Alloc = Total - Free)。
请注意,大页一旦被(分配或者预定),即使你将系统的 vm.nr_hugepages
参数调小,这些页面也依然会被保留,直到使用完毕。
因此,如果你想要真正回收这些大页,需要重启 PostgreSQL 服务。
./node.yml -t node_tune -e node_hugepage_count=3000 # 分配 3000 大页
精准分配大页
在 PostgreSQL 启动前,您需要分配 足够多的 大页,否则 PostgreSQL 将无法使用这些大页。
在 Pigsty 中,默认使用的 SharedBuffer 不超过内存的 25% ,所以您可以分配 26% ~ 27% 的内存作为大页,以确保 PostgreSQL 可以使用大页。
node_hugepage_ratio: 0.27 # 先分配 27% 内存作为大页,肯定够 PG 用了
如果不在乎少量资源浪费,您可以直接分配 27% 左右的内存作为大页。
回收脚本
PG 启动后,使用以下 SQL 可以查询到 PostgreSQL 实际使用的大页数量:
SHOW shared_memory_size_in_huge_pages;
最后,您可以精确指定所需的大页数量:
node_hugepage_count: 3000 # 精确分配 3000 个 2MB 大页(6GB)
然而,要精准的一个不漏的统计所需的大页数量,通常要等到 PostgreSQL 服务器启动后才能获取。
所以折中的办法是,提前超量分配大页启动 PostgreSQL 后,从 PG 中查询得到所需的精准大页数量,然后再精确修改所需大页的数量。
让PG独占大页
默认情况下,所有进程都可以去使用大页,如果用户希望仅允许 PostgreSQL 数据库使用大页,可以修改 vm.hugetlb_shm_group
内核参数
你可以调整 node_sysctl_params
参数,将 PostgreSQL 的 GID 填入。
node_sysctl_params:
vm.hugetlb_shm_group: 26
node_sysctl_params:
vm.hugetlb_shm_group: 543
注意 EL/Debian PostgreSQL UID/GID 默认值不同,分别为
26
,543
(可以显式通过pg_dbsu_uid
修改)
想要移除此变更:
sysctl -p /etc/sysctl.d/hugepage.conf
快速调整脚本
浪费的大页部分可以使用 pg-tune-hugepage
脚本对其进行回收,不过此脚本仅 PostgreSQL 15+ 可用。
如果你的 PostgreSQL 已经在运行,你可以使用下面的办法启动大页(仅 PG15+ 可用):
sync; echo 3 > /proc/sys/vm/drop_caches # 刷盘,释放系统缓存(请做好数据库性能受到冲击的准备)
sudo /pg/bin/pg-tune-hugepage # 将 nr_hugepages 写入 /etc/sysctl.d/hugepage.conf
pg restart <cls> # 重启 postgres 以使用 hugepage
执行 pg-tune-hugepage
的样例输出:
$ /pg/bin/pg-tune-hugepage
[INFO] Querying PostgreSQL for hugepage requirements...
[INFO] Added safety margin of 0 hugepages (5168 → 5168)
[INFO] ==================================
PostgreSQL user: postgres
PostgreSQL group ID: 26
Required hugepages: 5168
Configuration file: /etc/sysctl.d/hugepage.conf
[BEFORE] ================================
Current memory information:
AnonHugePages: 8192 kB
ShmemHugePages: 0 kB
FileHugePages: 0 kB
HugePages_Total: 10025
HugePages_Free: 9896
HugePages_Rsvd: 5039
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 20531200 kB
Current sysctl settings:
vm.hugetlb_shm_group = 26
vm.nr_hugepages = 10025
vm.nr_hugepages_mempolicy = 10025
[EXECUTE] ===============================
Writing new hugepage configuration...
Applying new settings...
vm.nr_hugepages = 5168
vm.hugetlb_shm_group = 26
[AFTER] =================================
Updated memory information:
AnonHugePages: 8192 kB
ShmemHugePages: 0 kB
FileHugePages: 0 kB
HugePages_Total: 5168
HugePages_Free: 5039
HugePages_Rsvd: 5039
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 10584064 kB
Updated sysctl settings:
vm.hugetlb_shm_group = 26
vm.nr_hugepages = 5168
vm.nr_hugepages_mempolicy = 5168
[DONE] ==================================
PostgreSQL hugepage configuration complete.
Consider adding the following to your inventory file:
node_hugepage_count: 5168
node_sysctl_params: {vm.hugetlb_shm_group: 26}