ORA-27104故障排查:Oracle共享内存与shmmax/shmall内核参数调整
ORA-27104: system-defined limits for shared memory was misconfigured。我估计搜到这句话的人不是刚装完 Oracle 准备startup就是刚调完内存参数重启数据库时撞上的。这个报错字面意思是“系统定义的共享内存限制配置错误”翻译成人话就是Oracle 想申请一块共享内存操作系统直接没放行。问题绝大多数出在内核参数kernel.shmmax、kernel.shmall身上也可能和大页HugePages配置有关。这篇文章我按故障处置笔记的方式来写从报错原理、定位方法、系统参数计算到真实案例一条龙讲清楚。不管你是专职 DBA、运维还是刚学 Oracle 的开发者看完应该能掌握一套标准处理思路下次遇到不至于再靠搜索碰运气。1. 从报错到原理ORA-27104 与共享内存内核参数之间的关系1.1 报错现场长什么样先说最常见的现场。你在 SQL*Plus 里执行SQL startup等了几秒钟终端直接弹出一段错误类似这样ORA-27104: system-defined limits for shared memory was misconfigured Additional information: 1946157056 Additional information: 1有时候Additional information只有一行有时候有两行。别小看这串数字它就是后面定位问题的第一线索。除了终端alert_SID.log里同样会记录这段信息路径一般在$ORACLE_BASE/diag/rdbms/dbname/SID/trace下。这个错误并不只在手动启动实例时出现。dbca 建库的最后阶段、srvctl start database启动集群数据库、Data Guard 备库自动拉起等场景只要实例在创建 SGA 时申请共享内存失败都有可能出现。一句话概括ORA-27104 是 Oracle 在创建 SGA 时shmget()系统调用失败后抛出的“翻译后”错误。1.2 Oracle 启动时如何获取共享内存Oracle 实例启动的核心工作之一就是根据sga_max_size、memory_max_target等参数向操作系统申请一块连续或可拆分的共享内存区域用来存放 SGA 中的 buffer cache、shared pool、large pool、Java pool 等组件。在 Linux/Unix 上这个申请动作会调用 System V 共享内存接口shmget()。shmget()有三个关键参数共享内存 key、共享内存大小 size、标志位 shmflg。当传入的 size 超过系统允许的单个共享内存段上限时shmget()会返回失败Oracle 拿到失败结果后就把这个操作映射成了我们看到的 ORA-27104。系统层面有几个参数直接限制这个申请行为内核参数作用默认值参考kernel.shmmax单个共享内存段允许的最大字节数各发行版差异大很多服务器默认只有几十 MB 到几 GBkernel.shmall系统范围内共享内存页总数单位是页通常数值比较大但某些精简系统会被调得很小kernel.shmmni系统范围内共享内存段的最大个数默认 4096一般不会先撞上需要解释一下页page的概念。Linux 物理内存按页管理常规页面大小是 4096 字节可以用getconf PAGE_SIZE查看。shmall的单位不是字节而是页数。很多人在配置时只改shmmax忘了同步shmall结果还是会启动失败这一点后面详细说。另外Oracle 11g 之后引入的自动内存管理AMM即MEMORY_TARGET依赖的是 POSIX 共享内存/dev/shm文件系统这类配置如果空间不足往往报的是 ORA-00845 而不是 ORA-27104。但如果你的环境既用了大页又用了 AMM报错可能会串场排查时要留意。1.3 为什么 Oracle 不自动把内存“拆小”申请很多初学者会问系统限制单个共享内存段 16GB而我的 SGA 是 32GBOracle 为什么不能拆成两个 16GB 的段来申请这个问题不是不能拆而是 Oracle 在设计上更倾向于用一个共享内存段承载整个 SGA。拆段会带来管理复杂度、性能损耗和不同段之间的地址映射问题。更重要的是很多内存组件需要连续的虚拟地址空间。所以从实际角度看让系统限制适配数据库需求是更稳妥的方向。Oracle 官方安装文档里有一句反复出现的建议kernel.shmmax应该设置为“足够大”最好大于等于 SGA 大小。理解了这一点后面配置参数时才不会犹豫。2. 三步快速定位一两分钟找到 ORA-27104 的真凶2.1 先把 Additional information 里的数字换算成 GB拿到报错后第一件事不是去翻配置文件而是看Additional information里的数字。这个数字通常就是 Oracle 尝试申请的共享内存大小单位是字节。比如1946157056这个数用 bash 换算awk BEGIN{printf %.2f GB\n, 1946157056/1024/1024/1024}结果大约是 1.81GB。再比如3758096384换算后是 3.5GB。一眼能看出是 SGA 大小级别的量。有时Additional information会有两行第一行像错误码第二行才像申请大小。如果遇到两行数字可以都算一遍或者直接去 alert log 看更多上下文。把申请大小算出来后立刻对比系统当前的shmmaxcat /proc/sys/kernel/shmmax如果“申请大小”明显大于“shmmax”八成就是shmmax太小的锅。这一步几乎能解决一半以上的案例。2.2 同步检查 shmall、/dev/shm 和数据库内存参数shmmax没问题不代表天下太平。接下来要用一组命令把系统侧的关键信息都拉出来# 当前系统共享内存限制 ipcs -lm # 内核参数 cat /proc/sys/kernel/shmmax cat /proc/sys/kernel/shmall cat /proc/sys/kernel/shmmni # 物理内存和 /dev/shm 大小 free -h df -h /dev/shm # 是否启用了大页 grep -i huge /proc/meminfo数据库侧的内存参数如果实例起不来可以通过 pfile 查看。有一种场景是你手头只有 spfile但实例没法正常启动sqlplus / as sysdba SQL create pfile/tmp/pfile_before.ora from spfile;这个操作不需要实例成功启动只要能找到 spfile 就行。生成的/tmp/pfile_before.ora里会有*.sga_max_size、*.sga_target、*.memory_max_target等参数直接对比系统和数据库两边的数值问题就看得比较清楚了。2.3 三种情况三种判断方向这里给一个快速判断表按报错现场的特征对号入座现象大概率原因处理思路申请字节数 shmmaxkernel.shmmax太小调大shmmax至少大于等于 SGA申请字节数 shmmax但实例仍报 ENOMEM 类错误kernel.shmall页数不足按shmall shmmax / 4096公式同步调大已启用 HugePages报错前后/proc/meminfo显示大页空闲不够大页池容量不足增加vm.nr_hugepages或改为use_large_pagesAUTO设置了MEMORY_TARGET但报错伴随 /dev/shm 写入失败AMM 共享内存文件系统空间不足增大/dev/shm或关闭 AMM 改回 SGA/PGA 手动管理备库、测试库从主库拷贝参数文件后启动失败参数文件内存配置超过本机物理内存上限下调该库sga_max_size/sga_target这里要强调一个容易误判的点shmmax够了不代表shmall够了。shmmax限制的是“单个段最大字节数”shmall限制的是“全系统共享内存总页数”。系统里可能有其他程序段已经占用了一部分页数Oracle 申请时一叠加就顶破了shmall。3. 系统级修复kernel.shmmax 与 shmall 的正确设置方法3.1 参数计算一个能直接套用的估算方式先明确一个原则不要把shmmax和shmall当作两组孤立参数来配它们服务于同一个目标——让 Oracle 能一次性申请到足够的共享内存。推荐的估算步骤是确认物理内存大小free -g。确认数据库实际要用的 SGA 上限即sga_max_size如果使用 AMM还要关注memory_max_target。将shmmax设置为大于等于 SGA 上限同时留出系统余量。一般建议专用数据库服务器shmmax可设为物理内存的 60%~80%只要不低于 SGA 即可。混布其他服务的机器建议设成“物理内存的一半以上且大于 SGA”不要贪心。按页大小换算shmallshmall shmmax / 4096举个例子。服务器物理内存 128GBSGA 规划 64GBshmmax 64 * 1024 * 1024 * 1024 68719476736shmall 68719476736 / 4096 16777216如果服务器物理内存 256GBSGA 规划 96GBshmmax 96 * 1024 * 1024 * 1024 103079215104shmall 103079215104 / 4096 25165824这两个值并不需要精确到“恰好等于 SGA”只要保证shmmax SGAshmall对应的总字节数大于shmmax通常就能顺利通过shmget()这一关。3.2 写入 /etc/sysctl.conf 并立即生效在 CentOS 7 / RHEL 7 之后sysctl 配置分散到了/etc/sysctl.d/目录下/etc/sysctl.conf只是其中一个入口。为了便于管理和还原我习惯单独建一个文件vi /etc/sysctl.d/99-oracle.conf内容示例# Oracle shared memory settings kernel.shmmax 68719476736 kernel.shmall 16777216 kernel.shmmni 4096 kernel.sem 250 32000 100 128 fs.file-max 6815744 fs.aio-max-nr 1048576 net.ipv4.ip_local_port_range 9000 65500 net.core.rmem_default 262144 net.core.rmem_max 4194304 net.core.wmem_default 262144 net.core.wmem_max 4194304其中kernel.sem和fs.aio-max-nr虽然不是 ORA-27104 的直接原因但 Oracle 安装时经常同时要求调整一次性配好能少踩坑。写入后执行sysctl -p /etc/sysctl.d/99-oracle.conf这条命令会立即加载该文件里的参数不需要重启操作系统。验证是否生效cat /proc/sys/kernel/shmmax cat /proc/sys/kernel/shmall输出值和配置一致说明系统侧已经放行。如果执行sysctl -p时出现类似“不允许将参数减小到当前值以下”的提示说明系统里已经存在比目标值更大的共享内存段在使用中。这种情况一般需要先停止相关进程释放共享内存或者直接重启操作系统后再加载配置。不过对于刚做完系统安装、还没启动数据库的场景通常不会撞上这个限制。3.3 启用 HugePages 时的额外约束大页机制是为了减少 TLB miss、降低页表开销内存大的生产库一般会启用。但大页配置是另一套逻辑它直接影响共享内存申请是否成功。先看/proc/meminfo里的大页信息grep -i huge /proc/meminfo关键字段有HugePages_Total、HugePages_Free、HugePages_Rsvd。每个大页默认大小通常是 2MB可以用grep Hugepagesize /proc/meminfo查看。假设 SGA 是 48GB大页 2MB那么需要的大页数量是48 * 1024 / 2 24576所以vm.nr_hugepages至少要设置到 24576 以上最好留一点余量比如 25000。如果有多个实例要把所有实例的 SGA 加起来再算。配置文件追加vm.nr_hugepages 25000然后执行sysctl -p /etc/sysctl.d/99-oracle.conf同时还要检查 Oracle 用户的 memlock 限制。如果 memlock 限制太小即使大页池够大Oracle 也无法把大页锁在内存中启动照样失败。常见的设置vi /etc/security/limits.conf追加oracle soft memlock unlimited oracle hard memlock unlimited修改后需要重新登录 oracle 用户或者用ulimit -l确认当前限制是否已变成 unlimited。这里有个容易踩的坑如果use_large_pages参数设置为ONLYOracle 会要求所有 SGA 都从大页分配一旦大页不够直接启动失败而且报错往往就是 ORA-27104。如果你不想在排查阶段被大页问题干扰可以先临时把use_large_pages改成AUTO让 Oracle 优先用大页、不够时回退普通页等实例起来了再去细化大页数量。4. 数据库侧协同调整让 SGA 参数与系统限制匹配4.1 数据库参数修改的两种路径系统参数调完之后如果数据库内存参数本身就定得过高比如物理内存 64GB、sga_max_size却设了 64GB数据库还是可能在启动时因为整体内存吃紧或系统余量不足而报错。这时候要反过来调整数据库侧参数。如果实例能进入 nomount 状态直接改sqlplus / as sysdba SQL startup nomount; SQL alter system set sga_max_size32G scopespfile; SQL alter system set sga_target32G scopespfile; SQL shutdown abort; SQL startup;注意sga_max_size是静态参数不能直接用scopeboth修改只能写到 spfile重启后生效。如果实例连 nomount 都进不去就用前面提到的 pfile 方式SQL create pfile/tmp/pfile_before.ora from spfile;手工编辑 pfile 中的*.sga_max_size、*.sga_target等参数然后SQL startup pfile/tmp/pfile_after.ora;确认能正常启动后再将修改后的参数回写 spfileSQL create spfile from pfile/tmp/pfile_after.ora;这种方式适合备库、DG 环境因为备库不能随意 shutdown或者主备参数文件不能直接共享。4.2 完整的启动验证流程修复完成后不要只盯着“能 startup 就万事大吉”建议走一遍完整验证确认系统参数已生效cat /proc/sys/kernel/shmmax cat /proc/sys/kernel/shmall free -g df -h /dev/shm确认大页状态grep -i huge /proc/meminfo启动实例并观察 alert logsqlplus / as sysdba SQL startup;实例起来后用ipcs检查共享内存段ipcs -m重点关注bytes列数值应该接近sga_max_size。执行一条简单 SQL 确认数据库可用SQL select name, open_mode from v$database;另外ipcs -m输出里如果看不到 oracle 用户的共享内存段说明实例根本没成功创建 SGA。这时候要回头再看 alert log 里是否还有新报错。4.3 ASM、RAC 与 Data Guard 下的差异ASM 实例是很多人在排查 ORA-27104 时容易忽略的一块。ASM 实例也有 SGA虽然通常不大1GB~4GB但如果你在安装或者维护时不小心把memory_target调大它同样会触发共享内存申请失败。处理方式一样要么调大系统内核参数要么把 ASM 实例的内存参数调小。RAC 环境里所有节点的内核参数必须同步修改。因为实例可能在不同节点间切换你只改了一个节点的/etc/sysctl.d/99-oracle.conf另一个节点却没改等实例漂移过去还是会起不来。Data Guard 场景最经典的问题是备库从主库拷贝了参数文件但备库物理内存比主库小。主库 SGA64GB备库只有 32GB 物理内存启动时必然报 ORA-27104。解决思路是单独为备库准备一套内存参数让备库的sga_max_size、sga_target降低到物理内存可承受的范围。Data Guard 允许主备库的内存参数不一致这不影响日志同步。5. 三个真实故障复盘ORA-27104 的典型现场与处理过程5.1 新装 11g 起不来默认 shmmax 太小一套内部测试环境CentOS 6物理内存 64GBOracle 11.2.0.4。dbca 建库进行到最后一步实例启动直接失败alert log 里出现ORA-27104: system-defined limits for shared memory was misconfigured Additional information: 37580963843758096384换算一下正好是 3.5GB这是 11g 初始 SGA 的常见大小。检查/proc/sys/kernel/shmmax发现只有33554432也就是 32MB。32MB 的限制显然不可能让 Oracle 创建 3.5GB 的共享内存段。处理方式很直接在/etc/sysctl.d/99-oracle.conf中设置kernel.shmmax 68719476736、kernel.shmall 16777216执行sysctl -p后重新执行 dbca建库顺利完成。这里要提醒的是很多云主机、模板镜像的内核参数并不是为 Oracle 调优过的。装完操作系统就急着装 Oracle往往会在建库阶段撞上这类问题。建议装 Oracle 前先把内核参数统一核对一遍不要等报错了再补救。5.2 把 SGA 从 16G 调到 48G 后重启失败一套生产库物理内存 128GB平时 SGA16GB运行一直正常。某次优化DBA 想把 SGA 调到 48GB。操作前没有检查shmmax直接执行了alter system set sga_max_size48G scopespfile; alter system set sga_target48G scopespfile;重启实例后报 ORA-27104。查shmmax发现只有34359738368也就是 32GB。系统限制 32GB数据库却申请 48GB不报错才怪。处理时有两个方向一是把shmmax提到 64GB 以上二是把 SGA 降回 16GB。考虑到业务确实需要更大缓存最终选择了提升系统参数并把 HugePages 也一并调优。修改后实例正常启动ipcs -m显示的共享内存段大小也变成了约 48GB。这个案例的教训很典型数据库参数和系统参数是协作关系改任何一边之前必须确认另一边能够承接。5.3 启用了大页反而更糟大页池小于 SGA另一套环境物理内存 64GBSGA48GB。DBA 为了性能启用了 HugePagesvm.nr_hugepages设置的是 8192也就是 16GB 的大页池。结果实例启动报 ORA-27104检查/proc/meminfo发现HugePages_Total是 8192但HugePages_Free的变化并不明显说明 Oracle 要的大页数量根本不够。这里的问题不是shmmax而是大页池容量小于 SGA。Oracle 在use_large_pagesONLY的情况下会要求所有 SGA 都从大页池分配大页池不够就直接失败。处理方法把vm.nr_hugepages调整到 25000约 48.8GB同时检查 oracle 用户的memlock限制追加unlimited后重新登录。再次启动实例一切正常。这个案例提醒我们不要看到 ORA-27104 就只盯着shmmax和shmall大页配置同样能造成这个报错。6. ORA-27104 常见问题速查表与易忽略细节6.1 排查时一眼定位的对照表报错现象可能原因处理方向Additional information数值接近 SGA且大于shmmaxkernel.shmmax太小调大shmmax至少不小于 SGAshmmax已够大但系统总共享内存页数不足kernel.shmall太小按shmall shmmax / 4096同步调大已启用 HugePages/proc/meminfo大页空闲不足大页池容量小于 SGA调大vm.nr_hugepages设置 memlock 为 unlimited设置了MEMORY_TARGET同时/dev/shm空间紧张AMM 无法分配共享内存扩大/dev/shm或改为 SGA/PGA 手动管理备库/测试库使用主库参数文件启动失败参数文件内存配置超过本机物理内存单独下调备库sga_max_size/sga_target多个 Oracle 实例共存其中一个启动失败系统共享内存总量被其他实例耗尽统一核算所有实例 SGA 总量重新调shmmax/shmall容器环境内启动 Oracle 失败容器的/dev/shm默认太小启动容器时增加--shm-size参数6.2 修完仍然报错时最容易忽略的四个细节细节一sysctl -p只对你指定的文件生效。如果你之前有多个配置文件都定义了同一个内核参数后加载的可能覆盖先加载的。最稳妥的方式是用sysctl -p /etc/sysctl.d/99-oracle.conf指定自己的文件再用cat /proc/sys/kernel/shmmax确认最终值。细节二容器环境下/dev/shm默认很小。Docker 默认只有 64MB如果你在里面跑 Oracle 并且打算使用自动内存管理即使系统shmmax配好了也可能因为/dev/shm不足导致启动失败。启动容器时可以加--shm-size8g或更大。细节三内存参数修改后必须重启实例才能彻底生效。sga_target这类参数虽然可以动态调整但sga_max_size是静态参数必须重启。有些同事改完参数直接执行alter system set sga_target32G scopeboth看到 current 两秒内没有报错就以为成功了其实实例重启后才会真正按新配置分配 SGA。细节四不要把 ORA-27104 和 ORA-27102 混为一谈。ORA-27102 的提示是 out of memory很多时候和大页不足、物理内存碎片有关。ORA-27104 更像是“系统配置限制”类错误。两者定位方向有区别如果按 ORA-27104 的路子调完参数还报错要再回头确认错误码是不是已经变成了 ORA-27102。6.3 一个值得养成的操作习惯处理 ORA-27104 到现在我的习惯是把Additional information里的数字先换算成 GB再和shmmax、SGA 大小做对比很多情况下问题一眼就能定位。真正麻烦的是那些叠加了 HugePages、容器限制、AMM 配置的环境这类问题只能一层层排除最怕的就是同时改多个参数。建议每次只改一个变量改完立即验证确认生效后再动下一个。这样即使出现问题也能快速回退不会越改越乱。如果你手头正好遇到这个报错先别急着反复重启实例。按顺序检查内核参数、数据库内存参数、/dev/shm 和 HugePages大部分情况十分钟内能搞定。如果验证完所有环节仍然报错记得把 alert log 里完整的错误堆栈、ipcs -lm输出、/proc/meminfo里的大页信息一起收集起来这些数据能让你少走很多弯路。