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 到几 GB |
kernel.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 是 32GB,Oracle 为什么不能拆成两个 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 看更多上下文。
把申请大小算出来后,立刻对比系统当前的shmmax:
cat /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 三种情况,三种判断方向
这里给一个快速判断表,按报错现场的特征对号入座:
| 现象 | 大概率原因 | 处理思路 |
|---|---|---|
| 申请字节数 > shmmax | kernel.shmmax太小 | 调大shmmax,至少大于等于 SGA |
| 申请字节数 < shmmax,但实例仍报 ENOMEM 类错误 | kernel.shmall页数不足 | 按shmall = shmmax / 4096公式同步调大 |
已启用 HugePages,报错前后/proc/meminfo显示大页空闲不够 | 大页池容量不足 | 增加vm.nr_hugepages,或改为use_large_pages=AUTO |
设置了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”,不要贪心。
- 专用数据库服务器:
- 按页大小换算
shmall:
shmall = shmmax / 4096举个例子。服务器物理内存 128GB,SGA 规划 64GB:
shmmax = 64 * 1024 * 1024 * 1024 = 68719476736shmall = 68719476736 / 4096 = 16777216
如果服务器物理内存 256GB,SGA 规划 96GB:
shmmax = 96 * 1024 * 1024 * 1024 = 103079215104shmall = 103079215104 / 4096 = 25165824
这两个值并不需要精确到“恰好等于 SGA”,只要保证shmmax >= SGA,shmall对应的总字节数大于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参数设置为ONLY,Oracle 会要求所有 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_size=32G scope=spfile; SQL> alter system set sga_target=32G scope=spfile; SQL> shutdown abort; SQL> startup;注意sga_max_size是静态参数,不能直接用scope=both修改,只能写到 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';确认能正常启动后,再将修改后的参数回写 spfile:
SQL> 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 log:
sqlplus / 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 场景最经典的问题是备库从主库拷贝了参数文件,但备库物理内存比主库小。主库 SGA=64GB,备库只有 32GB 物理内存,启动时必然报 ORA-27104。解决思路是单独为备库准备一套内存参数,让备库的sga_max_size、sga_target降低到物理内存可承受的范围。Data Guard 允许主备库的内存参数不一致,这不影响日志同步。
5. 三个真实故障复盘:ORA-27104 的典型现场与处理过程
5.1 新装 11g 起不来:默认 shmmax 太小
一套内部测试环境,CentOS 6,物理内存 64GB,Oracle 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,平时 SGA=16GB,运行一直正常。某次优化,DBA 想把 SGA 调到 48GB。操作前没有检查shmmax,直接执行了:
alter system set sga_max_size=48G scope=spfile; alter system set sga_target=48G scope=spfile;重启实例后报 ORA-27104。查shmmax,发现只有34359738368,也就是 32GB。系统限制 32GB,数据库却申请 48GB,不报错才怪。
处理时有两个方向:一是把shmmax提到 64GB 以上;二是把 SGA 降回 16GB。考虑到业务确实需要更大缓存,最终选择了提升系统参数,并把 HugePages 也一并调优。修改后实例正常启动,ipcs -m显示的共享内存段大小也变成了约 48GB。
这个案例的教训很典型:数据库参数和系统参数是协作关系,改任何一边之前必须确认另一边能够承接。
5.3 启用了大页反而更糟:大页池小于 SGA
另一套环境,物理内存 64GB,SGA=48GB。DBA 为了性能启用了 HugePages,vm.nr_hugepages设置的是 8192,也就是 16GB 的大页池。结果实例启动报 ORA-27104,检查/proc/meminfo发现HugePages_Total是 8192,但HugePages_Free的变化并不明显,说明 Oracle 要的大页数量根本不够。
这里的问题不是shmmax,而是大页池容量小于 SGA。Oracle 在use_large_pages=ONLY的情况下,会要求所有 SGA 都从大页池分配,大页池不够就直接失败。
处理方法:把vm.nr_hugepages调整到 25000(约 48.8GB),同时检查 oracle 用户的memlock限制,追加unlimited后重新登录。再次启动实例,一切正常。
这个案例提醒我们,不要看到 ORA-27104 就只盯着shmmax和shmall,大页配置同样能造成这个报错。
6. ORA-27104 常见问题速查表与易忽略细节
6.1 排查时一眼定位的对照表
| 报错现象 | 可能原因 | 处理方向 |
|---|---|---|
Additional information数值接近 SGA,且大于shmmax | kernel.shmmax太小 | 调大shmmax,至少不小于 SGA |
shmmax已够大,但系统总共享内存页数不足 | 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-size=8g或更大。
细节三:内存参数修改后必须重启实例才能彻底生效。sga_target这类参数虽然可以动态调整,但sga_max_size是静态参数,必须重启。有些同事改完参数直接执行alter system set sga_target=32G scope=both,看到 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里的大页信息一起收集起来,这些数据能让你少走很多弯路。