☰
Linux生产环境Oracle HugePages配置实战:大内存性能调优避坑指南
2026/10/12 14:21:00 网站建设 项目流程

简介:这份PDF教程面向需要在Linux环境下为Oracle数据库等高性能应用配置HugePages的运维与DBA人员,旨在解决大内存场景下页表开销高、SGA被交换导致性能下降等实际问题。文档以Oracle 11g R2、RHEL 6.8、512GB物理内存、SGA 400G的测试环境为例,完整演示了从memlock无限制设置、vm.nr_hugepages参数调整到sysctl -p与重启生效的操作链路,并给出hugepages_settings.sh脚本的具体内容与计算逻辑,帮助读者快速生成推荐值;同时针对ASM/ASMM选择、pga_aggregate_target不计入SGA、数据库SGA大小变更后的重新配置等易错点给出了明确提醒,实用性很强。资源为单份PDF文档,压缩包仅53KB,轻量便携,适合下载后随时翻阅。目前已有1704人学习该资料,步骤说明与配置命令逐条对应,能大幅减少新手在HugePages配置上的试错成本,尤其适合需要快速在服务器上落地大页内存优化的工程师参考。

1. Linux 生产环境快速配置 HugePages:一次 Oracle 大内存踩坑实录

在 Linux 服务器上快速配置 HugePages,是每个要做 Oracle 性能调优的 DBA 都绕不开的活。当物理内存到 512G、Oracle SGA 开到 400G 这个量级,如果还让数据库用普通 4K 页管理共享内存,页表开销会吃掉大量 CPU 和内存带宽,系统响应像踩了棉花。HugePages 的原理并不复杂:用 2MB 甚至更大尺寸的页替代 4K 小页,减少页表层级和 TLB miss,让 400G 的 SGA 只占用少量页表项。我记录的是在 RHEL 6.8 + 512G 物理内存、Oracle 11.2.0.4 SGA=400G 环境下的完整配置过程,重点拆成三步:设 memlock、用官方脚本算 vm.nr_hugepages、验证 HugePages 真的被实例用上。适合给已经了解基本概念、准备在真实环境动手的 DBA 和运维参考。

2. 为什么先调 memlock:limits.conf 与 Oracle 用户内存锁定的几个细节

2.1 memlock 的定位:它锁住的是内存还是权限

排障的时候,最容易看漏的就是/etc/security/limits.conf。这个文件通过 PAM 模块对用户会话设置资源上限,其中memlock控制的是"进程允许锁定内存地址空间的上限",单位是 KB。注意,它不是直接分配内存,而是给了一个权限边界。

Oracle 实例在启动时要把 SGA attach 到共享内存段,并且用内存锁定机制把页固定在物理内存里,防止被 swapd 换到磁盘。SGA 都到 400G 了,如果 memlock 还是默认的 32KB 或 64KB,进程根本锁不住这么大范围,HugePages 再多也用不上。

vi /etc/security/limits.conf # 在文件末尾追加 oracle soft memlock unlimited oracle hard memlock unlimited

这里有两点要注意。第一,soft和hard都要设,不然会话启动时按 soft 限制走,ulimit -l看到的还是小值;第二,用户名必须和实际跑 Oracle 实例的操作系统用户一致,如果实例是 grid 用户跑的 ASM,那 grid 用户也要单独加一行。我见过不少人只给 oracle 加,结果 ASM 实例起来时依旧报内存锁定不足。

验证方式很简单,新开一个 oracle 用户的会话,执行ulimit -l,能看到unlimited就说明 limits.conf 生效了。如果看不到,检查文件语法,确认没有多余空格,然后确认/etc/pam.d/login或/etc/pam.d/sshd里包含pam_limits.so这一行。RHEL 6 默认都有,但有些加固过的系统会把这行去掉。

2.2 HugePages 分配的两个前置条件:内核参数与页表预留

memlock 只是第一步。HugePages 跟普通内存不一样的地方在于,它不是一个"用到时再分配"的内存池,而是系统启动后,内核就按vm.nr_hugepages的值预留好连续页框。这个预留动作是全局的,不区分用户。所以你既要有 memlock 保证 Oracle 能用这些页,又要有足够大的 nr_hugepages 保证池子够用。

还有个容易混淆的概念是透明大页(THP)。RHEL 6 默认开启 THP,它做的是"自动把透明巨页映射给进程",看着像 HugePages,但对 Oracle 来说 THP 会导致内存回收和延迟不稳定,官方是明确建议关闭的。在配置业务库之前,我一般会先到/etc/sysctl.conf里确认或添加:

vm.nr_hugepages = 0 vm.swappiness = 10

先把 THP 关掉:echo never > /sys/kernel/mm/transparent_hugepage/enabled,再写入/etc/rc.d/rc.local固化,然后再来处理 HugePages。这一步很多人会跳过去,后续出现性能抖动又回来说"玄学"。其实不是玄学,是页表路径上两层机制在打架。

2.3 memlock 与 HugePages 的先后顺序:先解锁再分配

实际操作顺序上,我的习惯是"先 memlock,后 nr_hugepages"。原因很简单:如果先把vm.nr_hugepages调大,内存池已经被固定占用,但 Oracle 启动时发现 memlock 不够,共享内存申请失败,实例直接起不来;反过来,memlock 先放开,最多只是给了权限,不会真锁内存,等池子配好实例一启动就锁上了。

这里还有个细节:/etc/security/limits.conf的修改只对之后登录的会话生效,已经在跑着的 oracle 进程不受影响。所以配置完 limits 后,Oracle 相关进程最好重启一次,或者至少重新登录后再启动实例。如果不想重启整个系统,监听和实例全部重启一轮,让新的 rlimit 落到进程上。

提示:用su - oracle -c 'ulimit -l'验证时,注意 su 不带-不会加载完整的 PAM 会话,看到的可能不是真实值。务必用su - oracle。

3. 用 hugepages_settings.sh 计算 vm.nr_hugepages:脚本原理与三种结果解读

3.1 脚本运行前的三个前置条件

hugepages_settings.sh是 MOS 文档 401749.1 提供的官方脚本,作用是扫描当前系统上所有共享内存段,算出刚好够用的页数。但脚本不是拿来就能跑的,它有三个硬性前置条件。

第一,Oracle 实例必须处于运行状态。脚本要靠ipcs -m的输出计算共享内存段字节数,实例没起,ipcs -m里只有零星几个系统段,算出来就是几百页,没有任何参考意义。第二,11g 的 AMM 必须关闭。AMM 把 SGA 放在/dev/shm的 tmpfs 文件系统上,不走共享内存段,脚本统计不到,而且 HugePages 也映射不上去。第三,目标 SGA 大小必须已经确认。如果接下来要改 SGA,那就先改后跑脚本,别用旧值算新数。

# 从 MOS 文档复制脚本内容保存,赋予执行权限 chmod +x hugepages_settings.sh ./hugepages_settings.sh

脚本运行后会先打印一段说明,提示按回车继续。回车之后,它依次执行uname -r取内核版本、grep Hugepagesize /proc/meminfo取大页大小、再遍历ipcs -m里的每个共享内存段做计算。

3.2 脚本的核心计算逻辑:从 ipcs -m 的字节数到页数估算

贴一段脚本里的核心循环,这段决定了最终数字:

for SEG_BYTES in `ipcs -m | cut -c44-300 | awk '{print $1}' | grep "[0-9][0-9]*"` do MIN_PG=`echo "$SEG_BYTES/($HPG_SZ*1024)" | bc -q` if [ $MIN_PG -gt 0 ]; then NUM_PG=`echo "$NUM_PG+$MIN_PG+1" | bc -q` fi done

ipcs -m输出的共享内存段信息里,前 43 个字符是权限、key 这些字段,从第 44 列开始才是段大小(字节)。脚本用cut -c44-300把这一列抠出来,awk '{print $1}'拿第一个字段,grep "[0-9]"过滤掉非数字行。得到每个段的字节数后,SEG_BYTES / (HPG_SZ * 1024)就是该段需要的大页页数。注意HPG_SZ的单位是 KB,所以乘以 1024 转成字节。

为什么每个段都要+1?因为bc的除法是向下取整,共享内存段不太可能正好是 2MB 的整数倍。多给 1 页,避免最后一段因为页数不够而被拒绝映射。这是脚本作者刻意留的余量,不是 bug。

NUM_PG累加完所有段后,再根据内核版本输出。2.6 和 3.x、4.x 内核统一输出vm.nr_hugepages,2.4 老内核输出的是vm.hugetlb_pool,那套配置已经过时了。

3.3 三种运行结果解读:12G、400G 与没有实例的情况

在同一台机器上,我分别跑了三次,结果差异很大,正好说明脚本的"状态敏感性":

运行前状态SGA_MAX_SIZE脚本输出
实例已启动12GRecommended setting: vm.nr_hugepages = 6148
实例已启动400GRecommended setting: vm.nr_hugepages = 204805
实例未启动无ERROR,提示没有足够的共享内存段

12G 的 SGA 算出来 6148 页,6148 × 2MB ≈ 12.008 GB,恰好覆盖 12G 加上几个额外段的余量。400G 算出 204805 页,204805 × 2MB ≈ 400.01 GB。这里多说一句,别以为 400G 就应该是 204800 页,脚本把当前所有共享内存段都算进去了,包括 ASM 实例、数据库实例、甚至手动创建的其他段。所以多出 5 页,是非常正常的。

没有实例时的报错长这样:

************* ** ERROR ** ************* Sorry! There are not enough total of shared memory segments allocated for HugePages configuration.

这个报错后面会详细讲,解决思路只有一个:先关闭 AMM,把实例拉起来,再重跑。跑完脚本,得到一个数值,下一步就是把它写进内核参数。

提示:如果机器上有多个 Oracle 实例,确保它们全部启动后再运行脚本。脚本统计的是"当前所有共享内存段",少一个实例,算出的页数就可能不够装下所有 SGA。

4. 把建议值写进 sysctl.conf 并验证生效:从内核参数到 Oracle alert 日志

4.1 写入 sysctl.conf 并执行 sysctl -p

脚本输出Recommended setting: vm.nr_hugepages = 204805后,把这行写进/etc/sysctl.conf。我习惯用追加的方式,避免 vi 编辑时手滑删掉别的参数:

echo 'vm.nr_hugepages = 204805' >> /etc/sysctl.conf sysctl -p

sysctl -p会重新加载全文件配置。对vm.nr_hugepages来说,执行时刻内核就会尝试分配这么多页。如果物理内存充足并且内存碎片不严重,/proc/meminfo中的 HugePages_Total 会立刻变成 204805。如果物理内存不够,内核不会报错,而是默不作声地分配一部分,静默失败。这是最坑的一点。

所以执行完sysctl -p后,第一件事不是看 alert 日志,而是先确认 Total:

grep Huge /proc/meminfo

看到HugePages_Total: 204805才说明池子真的建起来了。如果 Total 偏小,先查dmesg | tail -20,看有没有内存分配失败或 Cgroup 限制的记录。

这个阶段不涉及数据库重启,因为 HugePages 是系统级配置,运行中的进程不会自动使用新池,只有新启动的共享内存段才能从中分配。所以 sysctl 生效后,还需要重启 Oracle 实例。

4.2 两个层面的验证:/proc/meminfo 与 alert 日志

我配置完后,grep Huge /proc/meminfo的输出是这样的:

AnonHugePages: 0 kB HugePages_Total: 204805 HugePages_Free: 168475 HugePages_Rsvd: 168471 HugePages_Surp: 0 Hugepagesize: 2048 kB

逐行解释一下:HugePages_Total等于我们配置的 204805,池子建好了。HugePages_Free是 168475,表示还没被真正占用的空闲页。HugePages_Rsvd168471,这些页已经被 Oracle 保留(reserve)但还没有被实际写入,所以 free 里依然能看到它们。HugePages_Surp是 0,说明没有超过配置值额外分配的页,状态正常。Hugepagesize是 2048 kB,也就是 2MB 大页。

光看 /proc/meminfo 还不够,它只能证明"池子建好了",不能证明"数据库真的用上了"。Oracle 在启动过程中,alert 日志会打印一段 Large Pages Information,那才是最终答案。我那次启动后日志片断如下:

Starting ORACLE instance (normal) ************************ Large Pages Information ******************* Per process system memlock (soft) limit = UNLIMITED Total Shared Global Region in Large Pages = 400 GB (100%) Large Pages used by this instance: 204801 (400 GB) Large Pages unused system wide = 4 (8192 KB) Large Pages configured system wide = 204805 (400 GB) Large Page size = 2048 KB ********************************************************************

看到 100% 这个数字就可以放心了:整个 SGA 全部由 HugePages 承载。Large Pages used by this instance: 204801说明实例用了 204801 页,剩下的 4 页被其他系统共享内存段占用或作为空闲余量。

如果这里的百分比是 0%,或者日志里根本没有 Large Pages Information,那问题多半出在 memlock 没生效或 AMM 没关干净。这时候别急着调大 nr_hugepages,先回头把 limits.conf 和memory_target检查一遍。

4.3 修改 SGA 后重新计算的步骤

实际运维中 SGA 总会调整。我的流程是:先停实例,把/etc/sysctl.conf中的vm.nr_hugepages改成 0,执行sysctl -p释放池子;然后再以新 SGA 启动实例,等共享内存段稳定后,再跑hugepages_settings.sh,把新数字写回去。为什么中间要改成 0?因为 HugePages pool 是固定大小的,如果旧池还有大量页被实例锁着,sysctl 改小会自动释放空闲页,但已经被实例 mapping 的页不会立刻归还,导致新值算出来不可靠。

注意:如果你调整了 SGA 大小但没重新计算,常见后果是实例在启动时因为 HugePages 空间不够而报错,或者反过来,池子太大浪费物理内存,触发 OOM。这两种情况在第 5 章集中看。

5. 避坑手册:AMM 冲突、ASM 实例与 SGA 变更后的典型故障

5.1 AMM 没关:配置全做了,实例却用不上大页

  • 现象:memlock 和 nr_hugepages 都配置正确,grep Huge /proc/meminfo也能看到 Total=204805,但 Oracle 实例启动后,alert 日志里的百分比是 0%,HugePages_Free数值几乎没变化。
  • 原因:11g 的 Automatic Memory Management(memory_target/memory_max_target)会把 SGA 放到/dev/shm的 tmpfs 上,而共享内存映射不经过 HugePages。实例启动时根本没走大页分配路径,池子建好也白搭。
  • 解决:在参数文件里把memory_target=0和memory_max_target=0显式写死,改用sga_target+pga_aggregate_target(即 ASMM 模式),然后重启实例。之后再跑脚本重算。

这是我在生产环境遇到最多的翻车现场,十个配置 HugePages 失败的人,有七个是 AMM 没关透。Oracle 官方对 11g 的说明里也反复强调 HugePages 与 AMM 不兼容,但参数默认没禁,很多人忽略了。

5.2 ASM 实例在场:脚本算出的页数偏小

  • 现象:主机上既有 ASM 实例也有数据库实例,按脚本建议值配置后,ASM 实例启动时报ORA-27102: out of memory,或者数据库能启动但 ASM 的共享内存段映射失败。
  • 原因:脚本统计的是运行中实例的共享内存段,如果跑脚本时 ASM 实例没启动,或者 ASM 实例的memory_target没清零,它对应的 SGA 就没被计入总数。HugePages 池只够分给数据库实例,ASM 启动时再要一份就分不出了。
  • 解决:先把 ASM 实例也用 ASMM 模式(sga_target明确设置),再把 ASM 和 DB 实例全部启动,最后跑脚本。如果只是给 ASM 库配,同样先起 ASM 再跑脚本,并且给 ASM 的 SGA 留出余量。

ASM 的 SGA 一般不大,通常几个 GB,但分配时机恰好在大页池边缘时,那 1-2 页的差异就会导致启动失败。遇到ORA-27102,先dmesg看有没有Out of memory,再ipcs -m对比当前共享内存总字节数,手动核一遍就知道是不是页数不够。

5.3 缩小 SGA 后没重算:HugePages 池子过大引发 OOM

  • 现象:原来 SGA=400G,后来业务调整降到 12G,只改了sga_target就重启了。实例倒是能启动,但过了一会儿系统内存越来越紧,最终操作系统 OOM-killer 把某些进程杀了。
  • 原因:vm.nr_hugepages = 204805还是旧值,400G 的物理内存被锁在大页池里。SGA 缩到 12G 后,池子里没用的那 388G 内存普通进程无法调用,系统的实际可用内存骤减,别的进程申请内存时触发 OOM。
  • 解决:改完 SGA 一定要重新跑脚本。临时释放池子:将vm.nr_hugepages改成 0,sysctl -p,等 HugePages_Free 涨到接近 Total,再把新建议值写回去。这个操作相当于给系统吃了颗后悔药,不用重启机器。

我第一次处理这个问题时,以为是内存泄漏,查了半天才发现大页池像个貔貅一样只进不出。从那以后,我每次调 SGA 都强制走一遍"改参数 -> 算页数 -> 核对 meminfo"的流程。

5.4 分配时静默失败:HugePages_Total 小于配置值

  • 现象:sysctl -p执行完,grep Huge /proc/meminfo里 Total 只有 180000,而配置值写的是 204805,内核没有报任何错误。
  • 原因:物理内存不足或内存碎片化严重时,内核没办法一次性凑出足够多的连续页框来满足 nr_hugepages,于是"有多少给多少"。这个行为是静默的,不会在 sysctl 输出里体现。
  • 解决:用dmesg | grep -i huge看有没有分配告警;如果有其他大内存进程占着物理内存,先停掉再重试。还可以用echo 204805 > /proc/sys/vm/nr_hugepages手动触发;或者把系统重启一次,在启动早期内存还没碎片化时让参数生效。

这个坑最隐蔽,因为配置流程全走完了,数据库也能起来,但 SGA 并没有 100% 用大页,性能提升打了折扣。所以我的习惯是:sysctl -p之后必须核对 Total,不能只看命令是否执行成功。

5.5 重启后配置消失:没写入 /etc/sysctl.conf

  • 现象:配置完一切正常,数据库也起来了,但重启服务器后,grep Huge /proc/meminfo里 Total=0,数据库启动报内存不足。
  • 原因:sysctl -w vm.nr_hugepages=204805只对当前运行期生效,没有写进/etc/sysctl.conf,重启后参数回滚。
  • 解决:确认/etc/sysctl.conf里有vm.nr_hugepages = 204805那行,并且没有被/etc/sysctl.d/下其他文件覆盖。RHEL 6 上默认只有一个主文件,但衍生系统上要检查自己的 sysctl 配置目录。

6. 进阶验证:用 ipcs -m 手动复核 HugePages 占用与映射关系

6.1 手算一遍:把共享内存段字节数加起来除以 2MB

hugepages_settings.sh不是万能的。在没有脚本、或者临时想验证它算得对不对时,我会用一条命令手动估算:

ipcs -m | awk '/0x/{print $5}' | awk '{s+=$1} END {printf "%.2f GB / %d pages\n", s/1024/1024/1024, s/2048/1024}'

这条命令的逻辑是:ipcs -m输出里,key 以0x开头的行就是共享内存段,$5是段大小字节数;累加后,除以 1024^3 得到 GB,除以 2048*1024(也就是 2MB)得到所需页数。在 400G 的环境下,结果应该接近 204800 页,再加上 3-5 页余量,就是脚本给的 204805。如果手算结果和脚本输出差出几十页以上,说明系统里有额外的共享内存段,或者脚本本身没有统计某些段,这时候要以ipcs的实际输出为准。

6.2 验证时的三个习惯动作

我每次配完 HugePages,都会按固定顺序做三件事。

第一,查看 limits.conf 是否生效,su - oracle -c 'ulimit -l',输出必须是unlimited。第二,grep Huge /proc/meminfo,Total 必须等于配置值,Surp 必须是 0。第三,接着重启实例,等实例起来后tail -50 $ORACLE_BASE/diag/rdbms/*/*/trace/alert_*.log,搜Large Pages Information,看到 100% 才算收官。

这三步做完,整个配置链路才闭环。前面任何一步没确认,后面出了问题排查范围都会大好几倍。

6.3 一个值得养成的习惯

从那次被 OOM 和静默分配坑过之后,我给自己定了一条规矩:凡是涉及 SGA 大小变更的操作,必须在变更前后各抓一份grep Huge /proc/meminfo和ipcs -m的快照,对比差异。变更前抓快照,是为了知道旧池子和旧共享内存段长什么样;变更后抓快照,是为了确认新实例有没有完全用大页池。这个习惯看着麻烦,实际就是三条命令的事,但在故障回溯时能省下大半天。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询