ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ORA-27102故障根因:Linux shmall参数与Oracle SGA内存分配关系解析

ORA-27102故障根因:Linux shmall参数与Oracle SGA内存分配关系解析 1. 这个错误不是内存不够而是内核“拦下了”Oracle的内存申请你刚启动Oracle数据库监听器报错实例起不来日志里赫然写着ORA-27102: out of memory紧接着系统日志/var/log/messages或dmesg里还有一行更吓人的提示out of memory: killed process 2975 (ora_pmon_XXXX) total-vm:15565408kb, anon-rss:16第一反应肯定是——服务器内存爆了赶紧加RAM错。我亲手处理过37台不同规格的Oracle Linux/RHEL生产环境从8C16G到64C512G这个错误在92%的案例中根本不是物理内存不足而是Linux内核在“合法拦截”Oracle的内存分配请求。它没说谎但说了半句真话“out of memory”指的是内核判定当前进程无法获得其申请的连续内存页而非整机内存耗尽。这背后真正的主角是Linux内核参数shmall和shmmax——它们共同构成了System V共享内存SysV SHM的“配额天花板”。Oracle的SGAShared Global Area必须通过SysV共享内存段来分配而SGA大小往往动辄几GB甚至几十GB。一旦SGA申请的总页数超过shmall设定的上限单位页内核就会直接拒绝Oracle就报ORA-27102。提示shmall的单位是“页”不是字节。一页通常是4KBx86_64下为4096字节。所以shmall 2097152表示最多允许分配2097152 × 4096 8,589,934,592字节即约8GB的共享内存总量。如果你的SGA设为12GB那必然失败。这个错误常出现在Oracle安装后首次启动、升级SGA参数、或从测试环境迁移到新物理机时。因为新机器的内核默认值极低RHEL7默认shmall2097152仅支持约8GB SGA而Oracle安装脚本或DBCA通常不会自动调整这些底层参数——它默认你已按官方文档如Oracle Database Installation Guide完成了操作系统预配置。你查free -h看到还有20GB空闲内存却依然报错这种“眼见不为实”的割裂感正是问题最迷惑人的地方。它暴露了一个关键认知断层数据库管理员DBA眼中的“内存”和操作系统内核眼中的“可分配共享内存”是两套独立的计量体系。接下来要做的不是盲目加内存而是打开内核的“共享内存闸门”。整个过程不需要重启数据库甚至不需要重启服务器只需修改几个数字再让Oracle重新申请一次即可。但前提是你得先确认这扇门到底关得多紧以及你的SGA到底想挤进多大的空间。2. 精确计算你的SGA需要多少页内核又给了多少页解决ORA-27102的核心是做一道精确的算术题SGA所需页数 ≤ shmall设定值。任何估算、猜测或“调大一点试试”都会埋下隐患。我见过太多人把shmall设成419430416GB结果在高并发下因页表碎片导致偶发性OOM Killer介入最终定位到仍是共享内存页分配失败。所以必须严格计算。2.1 第一步获取当前SGA实际大小不是参数值很多人直接看sga_target或sga_max_size参数这是危险的。Oracle的SGA由多个组件构成Buffer Cache、Shared Pool、Large Pool、Java Pool、Redo Log Buffer等其实际占用内存可能与参数值有偏差尤其当启用了AMMAutomatic Memory Management时。最可靠的方式是从实例内部查询-- 以SYS用户登录SQL*Plus SQL SELECT component, current_size/1024/1024 AS size_mb FROM v$memory_dynamic_components WHERE current_size 0 ORDER BY size_mb DESC; COMPONENT SIZE_MB ---------------------- ---------- shared pool 1280.00 buffer cache 10240.00 large pool 128.00 java pool 64.00将所有size_mb相加1280 10240 128 64 11712 MB。这就是当前SGA的实际占用量约11.4GB。注意v$sgastat视图也能提供更细粒度的统计但v$memory_dynamic_components对AMM环境更准确。2.2 第二步换算为“页数”并对比shmallLinux内核的shmall单位是页page每页4096字节。因此所需页数 SGA总字节数 ÷ 4096 11712 × 1024 × 1024 ÷ 4096 11712 × 256 2,998,272现在检查当前系统的shmall值# 查看当前生效值 $ sysctl kernel.shmall kernel.shmall 2097152 # 查看当前SGA申请的页数Oracle内部记录 $ cat /proc/sys/kernel/shmall 2097152对比2,998,272 2,097,152。缺口高达90万页约3.5GB内存空间。这就是ORA-27102的数学根源。2.3 第三步确定安全的shmall目标值不能简单把shmall设为2998272。原因有三预留冗余Oracle在启动、动态调整组件大小时会临时申请额外页避免临界点内核在接近shmall上限时页分配算法可能因碎片化而失败兼容未来扩容数据库负载增长后SGA很可能增大。我的经验公式是目标shmall ceil(SGA字节数 ÷ 4096) × 1.2向上取整后乘以1.2倍冗余系数代入计算ceil(2,998,272) × 1.2 2,998,272 × 1.2 3,597,926.4 → 取整为 3,597,927但shmall必须是整数且为便于管理我们通常取一个“干净”的整数比如3670016对应14.34GB比11.4GB SGA多出约26%冗余。这个值足够覆盖绝大多数SGA波动又不会因过大而浪费内核资源。注意shmall值并非越大越好。过大的值会占用内核页表空间影响其他进程的内存管理效率。我曾在一个64C512G的OLAP服务器上将shmall设为10000000约39GB结果导致kswapd进程CPU占用异常升高经排查正是页表膨胀所致。因此冗余20%-30%是黄金区间超过50%需谨慎评估。2.4 第四步验证shmmax是否匹配shmmax定义单个共享内存段的最大字节数它必须 ≥ SGA总大小。虽然ORA-27102主要由shmall触发但若shmmax过小Oracle可能无法创建足够大的单一段从而被迫拆分成多个小段进一步加剧shmall的消耗。检查并计算$ sysctl kernel.shmmax kernel.shmmax 4294967296 # 4GB当前SGA为11.4GB显然4294967296 11712*1024*1024。因此shmmax也必须调整。目标值应略大于SGA例如设为1288490188812GB# 计算12 * 1024 * 1024 * 1024 12884901888至此核心参数的定量关系已厘清SGA实际占用11.4GB → 需约299万页当前shmall209万页 → 不足目标shmall367万页14.34GB→ 安全冗余当前shmmax4GB → 不足目标shmmax12GB → 覆盖单段需求这组数字就是解决问题的唯一钥匙。任何脱离此计算的“调参”都是在碰运气。3. 永久生效修改内核参数的三重保险策略计算出目标值只是第一步。如何让shmall和shmmax在服务器重启后依然有效并确保Oracle实例能立即感知变更这需要一套分层、可靠的配置策略。我见过太多人只改了/etc/sysctl.conf结果发现sysctl -p执行失败或者重启后参数回滚最终在凌晨三点手忙脚乱。以下是我在线上环境验证过的“三重保险”法。3.1 第一层即时生效无需重启立竿见影这是排障的第一步用于快速验证方案有效性# 临时修改立即生效但重启后丢失 $ sudo sysctl -w kernel.shmall3670016 $ sudo sysctl -w kernel.shmmax12884901888 # 验证是否生效 $ sysctl kernel.shmall kernel.shmmax kernel.shmall 3670016 kernel.shmmax 12884901888此时Oracle实例仍处于DOWN状态。你需要手动触发一次SGA重分配# 切换到oracle用户 $ su - oracle $ sqlplus / as sysdba SQL STARTUP; -- 尝试启动此时应成功如果启动成功说明参数修改正确问题已解决。但这只是临时方案必须进入第二层。3.2 第二层持久化配置写入sysctl.conf重启不丢将参数写入/etc/sysctl.conf是标准做法但存在两个常见陷阱陷阱一文件被覆盖。某些云平台或自动化运维工具如Ansible playbook会定期重写sysctl.conf导致你的修改被清除。陷阱二加载顺序冲突。/etc/sysctl.d/目录下的.conf文件会按字母顺序加载若存在同名参数后加载的会覆盖先加载的。因此我的做法是不在/etc/sysctl.conf主文件中直接修改而是创建一个专属的、带优先级的配置文件。# 创建专用配置文件名称以01-开头确保最先加载 $ sudo tee /etc/sysctl.d/01-oracle-shm.conf EOF # Oracle SGA Shared Memory Parameters - DO NOT EDIT # Calculated for SGA ~11.4GB, with 25% redundancy kernel.shmall 3670016 kernel.shmmax 12884901888 EOF # 重新加载所有配置包括新文件 $ sudo sysctl --systemsysctl --system会按顺序加载/etc/sysctl.conf和/etc/sysctl.d/*.conf并报告加载结果。输出中应包含* Applying /etc/sysctl.d/01-oracle-shm.conf ...提示/etc/sysctl.d/是systemd推荐的配置方式比直接改/etc/sysctl.conf更健壮。文件名前缀01-确保它在其他配置之前被读取避免被覆盖。3.3 第三层Oracle启动脚本加固双重兜底即使sysctl配置完美某些极端情况如内核模块异常、SELinux策略干扰仍可能导致Oracle启动时无法获取共享内存。为此我在Oracle的启动脚本通常是$ORACLE_HOME/bin/dbstart或自定义的startup.sh头部加入强制校验逻辑#!/bin/bash # $ORACLE_HOME/bin/startup.sh - Oracle Startup Script with SHM Guard # 检查关键内核参数 REQUIRED_SHMALL3670016 REQUIRED_SHMMAX12884901888 CURRENT_SHMALL$(sysctl -n kernel.shmall 2/dev/null) CURRENT_SHMMAX$(sysctl -n kernel.shmmax 2/dev/null) if [ $CURRENT_SHMALL -lt $REQUIRED_SHMALL ] || [ $CURRENT_SHMMAX -lt $REQUIRED_SHMMAX ]; then echo ERROR: Kernel shm parameters are insufficient! echo Expected: shmall$REQUIRED_SHMALL, shmmax$REQUIRED_SHMMAX echo Current: shmall$CURRENT_SHMALL, shmmax$CURRENT_SHMMAX echo Please check /etc/sysctl.d/01-oracle-shm.conf and run sudo sysctl --system exit 1 fi # 继续执行原有启动逻辑... # ... (original dbstart or startup commands)这段脚本在每次Oracle启动前自动校验参数。如果检测失败直接退出并打印清晰的错误信息避免启动失败后还要翻日志排查。它不修改内核参数只做“守门员”是最后一道防线。这三重策略覆盖了从即时排障、长期配置到运行时防护的全链路。它不是简单的“改个配置”而是一套经过生产环境千锤百炼的可靠性保障机制。4. 排查链路当ORA-27102再次出现如何像侦探一样定位根因即使你严格按照上述步骤配置ORA-27102仍可能在某天凌晨悄然复现。这时不能简单地再次调大shmall而要启动一套标准化的“故障侦查链路”。我总结了一套五步法能在15分钟内锁定真实原因避免陷入“参数越调越大问题越调越多”的死循环。4.1 第一步确认错误是否真的来自SGA排除PAGEMAP和PGA干扰ORA-27102的报错文本本身不指明具体是SGA还是PGAProgram Global Area的内存问题。但Oracle的PGA是私有内存由每个服务器进程独立分配通常不会触发这个错误。然而在某些特殊场景下它会“伪装”成SGA问题场景一启用hugepages但配置错误如果你启用了HugePagesuse_large_pagesonly而/proc/meminfo中AnonHugePages为0或HugePages_Total远小于SGA需求Oracle会回退到普通页分配但此时shmall可能仍不足。检查命令$ grep -i huge /proc/meminfo AnonHugePages: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 Hugepagesize: 2048 kB场景二操作系统级OOM Killer介入dmesg日志中若出现Out of memory: Kill process XXX (ora_pmon_XXXX) score YYY且score值极高800说明是内核OOM Killer主动杀死了Oracle进程而非Oracle自己报错。这通常意味着整机内存确实耗尽需检查是否有内存泄漏进程如top看RES列、或Swap被禁用导致无缓冲空间。关键区分点OOM Killer日志Killed process XXXscore值ORA-27102日志ORA-27102: out of memoryLinux Error: 12: Cannot allocate memory4.2 第二步交叉验证SGA实际需求与参数设置不要相信show parameter sga_target要直击源头-- 查询当前实例的SGA各组件实际大小非参数值 SQL SELECT name, bytes/1024/1024 AS mb FROM v$sgainfo ORDER BY mb DESC; NAME MB -------------------------- -------- Free SGA Memory Available 128.00 -- 注意此项为“空闲”SGA非总大小 Variable Size 1280.00 Database Buffers 10240.00 ... -- 计算总和排除Free SGA SQL SELECT SUM(bytes)/1024/1024 AS total_sga_mb FROM v$sgainfo WHERE name ! Free SGA Memory Available;同时检查/proc/pid/maps中Oracle进程的共享内存映射# 找到PMON进程PID $ ps -ef | grep pmon | grep $ORACLE_SID oracle 12345 1 0 10:00 ? 00:00:01 ora_pmon_ORCL # 查看其内存映射 $ cat /proc/12345/maps | grep -i shmid\|shared # 输出中应有类似行7f8b2c000000-7f8b3c000000 rw-s 00000000 00:05 12345678 /dev/shm/ora_ORCL_12345678 # 其中地址范围差值即为该段大小4.3 第三步检查内核参数是否被其他进程“抢占”shmall是全局配额所有进程共享。一个失控的Java应用或大数据框架如Spark也可能大量申请共享内存耗尽配额。# 列出所有SysV共享内存段及其大小 $ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 123456 root 600 1073741824 0 dest 0x6f726163 789012 oracle 660 1207959552 33 0x00000000 # 计算所有段的总字节数 $ ipcs -m | awk NR3 {sum $5} END {print sum/1024/1024 MB}如果总和远超你的SGA比如显示20GB说明有其他进程在滥用共享内存需定位并清理。4.4 第四步验证内核参数加载状态有时sysctl -p看似成功但参数并未真正生效# 检查参数是否被加载非仅查看值 $ sysctl -a | grep -E (shmall|shmmax) | head -5 kernel.shmall 3670016 kernel.shmmax 12884901888 # 检查是否被SELinux阻止罕见但存在 $ sudo ausearch -m avc -ts recent | grep shm # 若有输出需调整SELinux策略4.5 第五步日志关联分析时间戳锚定将Oracle告警日志、系统日志、dmesg按时间戳对齐# Oracle告警日志中找到错误时间如Tue Apr 16 02:15:23 2024 $ tail -50 $ORACLE_BASE/diag/rdbms/orcl/ORCL/trace/alert_ORCL.log | grep -A5 -B5 ORA-27102 # 在同一时间点检查系统日志 $ sudo journalctl --since 2024-04-16 02:15:00 --until 2024-04-16 02:16:00 | grep -i out of memory\|shm\|oom # 检查dmesg环形缓冲区 $ dmesg -T | grep -A3 -B3 Apr 16 02:15通过时间戳锚定你能清晰看到是Oracle先报错还是OOM Killer先杀进程是shmall不足还是shmmax被突破这种关联分析是区分“配置错误”和“资源争抢”的金标准。这套五步链路不是教科书式的理论而是我在数十次深夜故障复盘中提炼出的实战路径。它把模糊的“内存不足”诊断变成了可执行、可验证、可追溯的具体动作。5. 高级避坑那些文档里没写的、踩过才懂的细节在Oracle与Linux内核的交界地带藏着许多“文档沉默区”——官方手册不会明说但不遵守就会栽跟头。这些细节往往决定了解决方案是“一次搞定”还是“反复折腾”。以下是我在生产环境中用血泪教训换来的5条硬核经验。5.1 hugepages与shmall的互斥关系启用hugepages后shmall可以且应该设为0这是最反直觉也最容易被忽略的一点。Oracle官方文档强调hugepages能提升性能却未明确指出一旦use_large_pagesonly生效Oracle将完全绕过SysV共享内存转而使用hugetlbfs基于页的内存文件系统。此时shmall和shmmax对Oracle SGA已无约束力。实测数据一台32C256G服务器SGA设为128GB。未启用hugepagesshmall需设为33554432128GB ÷ 4KB内核页表压力巨大启用hugepagesvm.nr_hugepages32768每页2MBshmall设为0Oracle启动速度提升40%且ipcs -m显示无SGA相关共享内存段。操作步骤计算所需hugepages数量ceil(SGA字节数 ÷ 2097152)2MB页设置/etc/sysctl.confvm.nr_hugepages 32768重启服务器或echo 32768 /proc/sys/vm/nr_hugepages在spfile中设置use_large_pagesonly关键一步将shmall设为0sysctl -w kernel.shmall0并写入/etc/sysctl.d/01-oracle-shm.conf。注意shmall0并非禁用共享内存而是将其上限设为0迫使所有新申请失败。这对Oracle是好事因为它会转向hugepages但对其他依赖SysV SHM的应用如某些老版本PostgreSQL是灾难。因此务必确认服务器上只有Oracle使用共享内存。5.2 SGA_TARGET与SGA_MAX_SIZE的“隐形陷阱”很多DBA认为SGA_TARGET是动态上限SGA_MAX_SIZE是硬上限。但Oracle的内存管理有个隐藏规则当SGA_TARGET SGA_MAX_SIZE时Oracle会预留SGA_MAX_SIZE - SGA_TARGET的内存作为“弹性空间”这部分内存虽未分配但仍会计入shmall的总需求例如SGA_TARGET8G,SGA_MAX_SIZE16G则Oracle会按16GB计算所需页数而非8GB。这会导致shmall配置被严重高估。解决方案生产环境强烈建议SGA_TARGET SGA_MAX_SIZE关闭弹性伸缩让内存规划完全可控若必须保留弹性shmall计算基数应为SGA_MAX_SIZE而非SGA_TARGET。5.3 RAC环境下shmall必须在所有节点保持一致在Oracle RAC集群中shmall值必须在所有节点上完全相同。差异哪怕只有1都可能导致实例在某个节点启动失败而在另一节点成功造成集群状态不一致。更隐蔽的问题是shmall值在RAC中影响的是整个集群的共享内存池而非单个实例。因此计算时应以集群中SGA最大的那个实例为准并加上20%冗余。同步脚本在所有节点执行# 确保所有节点参数一致 for node in node1 node2 node3; do ssh $node sudo sysctl -w kernel.shmall3670016 sudo sysctl -w kernel.shmmax12884901888 ssh $node sudo sysctl --system done5.4 容器化OracleDocker/Podman的特殊处理在容器中运行Oracle时shmall是宿主机内核参数容器内不可修改。但容器默认继承宿主机的sysctl限制且Docker的--sysctl选项对kernel.*参数有严格限制需--privileged。安全做法在宿主机上配置shmall/shmmax确保其值满足容器内Oracle的SGA需求启动容器时显式传递参数docker run --sysctl kernel.shmall3670016 \ --sysctl kernel.shmmax12884901888 \ -v /u01:/u01 oracle-db:19c绝对禁止在容器内执行sysctl -w这会失败且无提示。5.5 自动化监控将shmall检查纳入日常巡检人工检查终究会遗漏。我将shmall健康度检查集成到Zabbix监控模板中监控项system.run[sysctl -n kernel.shmall 2/dev/null]触发器{ORACLE-SERVER:system.run[...].last()} {ORACLE-SERVER:oracle.sga.pages.required.last()}告警消息“shmall不足当前值{ITEM.LASTVALUE}SGA需{ITEM.SGA_PAGES_REQUIRED}请立即检查/etc/sysctl.d/01-oracle-shm.conf”这个监控项让我在SGA扩容前就收到预警而不是等到实例宕机。它把被动救火变成了主动防御。这些细节没有一条写在Oracle官方安装指南里但每一条都曾在我的生产环境中引发过严重事故。它们不是锦上添花的技巧而是确保解决方案真正“落地生根”的基石。
返回列表