注: 本文为安丫科技刘峰的原创,请尊重知识产权,转发请注明出处,不接受任何抄袭、演绎和未经注明出处的转载。
做数据库运维,最怕遇到这种“罗生门”式的故障:当你火急火燎地启动 Oracle 实例,屏幕上冷冰冰地甩给你一行 ORA-27102: out of memory,紧接着 Linux 内核补了一刀 Error 28: No space left on device。
你的第一反应肯定是:“完蛋,内存爆了?还是磁盘满了?”
可是当你颤抖着敲下 free -g 和 df -h,却发现物理内存还剩一大半,磁盘空间也空空荡荡。
那一刻,是不是有一种被系统“针对”的无力感?明明有资源,为什么数据库睁眼说瞎话?
别急,这根本不是物理内存不够,而是 Oracle 想要的那块“特殊地盘”被内核卡住了。
今天,我们就来扒一扒这个让无数 DBA 掉坑的经典故障,带你从原理到实战,彻底终结这个“假内存不足”的幽灵,让你在排障时不再迷茫。
当尝试启动一个配置有较大 SGA 的 Oracle 实例时,可能会遇到以下错误:
ORA-27102: out of memoryLinux-x86_64 Error: 28: No space left on device
核心误区
将此错误等同于物理内存(RAM)不足。
Error 28 (ENOSPC) 在此上下文中,并非指磁盘空间,而是指内核在为 Oracle 分配其所需的特定类型内存资源池时,该池“空间不足”。
这个资源池可能是 System V 共享内存、tmpfs 文件系统(/dev/shm),或最常见的 HugePages 内存池。
核心原理剖析:Oracle SGA 与
Linux 内存分配机制
要理解该错误,必须先深入了解 Oracle SGA 的特性及其在 Linux 上的实现方式。
SGA(System Global Area)是 Oracle 实例的核心内存组件,用于缓存数据块、执行计划、SQL 解析结果等。
其关键特性是:
现代数据库的 SGA 通常以数十甚至数百 GB 计。
SGA 必须被实例的所有后台进程和服务器进程共享访问。
性能敏感(Performance-Sensitive)
这些特性决定了 SGA 的分配不能像普通应用程序内存那样随意。
Oracle 必须向操作系统申请一块巨大的、连续的内存空间,并确保其高效、稳定。
Linux 为 Oracle 提供的三种内存分配模型
根据 Oracle 的配置,它会选择以下三种模型之一来向 Linux 内核申请 SGA 内存。
模型一:System V Shared Memory (经典模型)
这是传统的 IPC(Inter-Process Communication)机制。
Oracle 请求内核创建一块或多块共享内存段,所有进程通过附加(attach)到这些段上来实现共享。
内核维护一个全局的共享内存段列表。
进程通过一个唯一的键(key)来创建或访问这些段。
相关内核参数
kernel.shmmax
单个共享内存段的最大允许大小(Bytes)。如果 SGA 大于此值,Oracle 会尝试创建多个段。
kernel.shmall
系统范围内所有共享内存段的总大小上限,单位是内存页(通常为 4KB)。
这是最关键的限制。
如果所有 Oracle 实例的 SGA 总和,加上系统中其他使用 System V 共享内存的应用所需空间,超过了 shmall * PAGE_SIZE,新的分配请求就会失败,并可能返回 ENOSPC 错误。
模型二:AMM (Automatic Memory Management) via tmpfs
当启用 AMM(设置 MEMORY_TARGET 和 MEMORY_MAX_TARGET)时,Oracle 改变了内存管理策略。
它会在一个基于内存的文件系统 tmpfs 上创建一个或多个文件,然后通过内存映射(mmap)的方式将这些文件映射到自己的地址空间,作为 SGA 和 PGA 的统一管理区域。
工作原理
在 Linux 上,tmpfs 的默认挂载点是 /dev/shm。
Oracle 在此目录下创建文件,文件的大小即为 SGA 和 PGA 的总和。
限制因素
此模型的瓶颈非常直接——/dev/shm 的挂载容量。
如果 MEMORY_TARGET 的值大于 /dev/shm 的可用空间,Oracle 在创建映射文件时就会失败,返回 ENOSPC。
对于大 SGA 环境,使用标准的 4KB 内存页会导致极大的性能开销。
因为 CPU 需要维护一个庞大的页表(Page Table)来映射虚拟地址和物理地址,这会增加 TLB(Translation Lookaside Buffer)的缓存未命中率,即“TLB-thrashing”。
HugePages(在 Linux 中也称 HugeTLB)机制允许操作系统以更大的单位(通常是 2MB 或 1GB)来管理内存。
工作原理
a. 预分配
系统管理员通过 vm.nr_hugepages 内核参数,在系统启动时预留一个固定大小的、连续的、不可交换的物理内存池。
b. 独占式申请
当 Oracle 配置为使用 HugePages(USE_LARGE_PAGES = TRUE 或 ONLY)时,它会向内核发出一个特殊的内存分配请求,明确要求从这个 HugePages 池中获取内存。
c. “All-or-Nothing”原则
这是导致 ORA-27102 的核心原则。
如果 HugePages 池中的可用页数不足以满足整个 SGA 的需求,Oracle 不会降级去使用标准的 4KB 页,而是会直接放弃启动,报告内存分配失败。
故障诊断与实战分析:250GB
SGA 启动失败的根因定位
现在,我们运用上述原理,对 250GB SGA 启动失败的案例进行精确诊断。
首先,检查 pfile 或 spfile,确认 MEMORY_TARGET 是否设置。
在本案例中,我们确认 MEMORY_TARGET 为 0,且 使用 HugePages。
这是控制 I/O 争抢的核心机制。
autovacuum 的工作受到一套成本模型的限制:
需求端(Oracle)
SGA 大小
sga_target = 254464M ≈ 250 GiB
HugePage 大小
通过 grep Hugepagesize /proc/meminfo 得到,通常为 2048 kB (2 MB)。
所需 HugePages 数量 = SGA 总大小 / HugePage 大小 = (250 * 1024 * 1024) KB / 2048 KB = 128,000 页
供应端(Linux OS)
检查 /proc/meminfo 的 HugePages 相关信息:
HugePages_Total: 130000HugePages_Free: 120691HugePages_Rsvd: 93604
HugePages_Total
系统总共配置了 130,000 个大页,理论上足够。
HugePages_Free
但当前物理上空闲的只有 120,691 个。
HugePages_Rsvd
更重要的是,有 93,604 个大页已被“预留”。
Rsvd (Reserved) 指的是已被某个进程通过 mmap 请求,但尚未被内核实际分配物理页的“逻辑占位”。
这些页面虽然计入 Free,但实际上已名花有主,不能被新进程使用。
供需对比分析:
直接对比
需求(128,000 页) > 物理空闲(120,691 页)。
仅此一点,分配请求就已注定失败。
考虑预留
实际可供支配的页数远小于 120,691。
一个保守的估算是 Free - Rsvd,即 120691 - 93604 = 27087 页。
这个数字与 128,000 的需求相比,差距巨大。
结论
Oracle 实例向内核申请 128,000 个大页,内核发现 HugePages 池中可用的页数严重不足,因此拒绝了请求,返回 ENOSPC (Error 28)。
即使 HugePages 数量足够,如果 oracle 用户没有将大块内存锁定在物理内存中的权限,启动同样会失败。
以 oracle 用户身份执行 ulimit -l。
该值(单位 KB)必须大于等于 SGA 的大小。
若为 unlimited,则无此限制。
HugePages_Rsvd 过高或 HugePages_Free 过低,通常意味着资源已被占用。
通过 ps -ef | grep pmon 检查,确认是否有其他 Oracle 实例(如 RAC 的另一个节点实例)正在运行。
如果实例曾被异常终止(kill -9 或 shutdown abort),可能会留下“僵尸”共享内存段。
通过 ipcs -m 查看,若发现属于 oracle 用户的残留段,它们会持续占用系统 IPC 资源。
根据诊断出的具体根因,可采取以下相应的修复策略。
所有命令均应在具有相应权限的用户下执行(通常是 root 或 oracle)。
这是最常见的场景。
解决方案的核心是“要么释放,要么增加”。
① 识别并正常关闭占用进程
首先,识别是哪个进程占用了 HugePages。
在 Oracle 环境中,通常是另一个数据库实例。
# 以 oracle 用户或 root 用户执行,查找正在运行的 Oracle 实例ps -ef | grep pmon# 示例输出,显示 eapdb1 实例正在运行# oracle 123451010:00 ? 00:00:01 ora_pmon_eapdb1
然后,正常关闭该实例以释放其占用的 HugePages。
# 以 oracle 用户执行# 设置正确的环境变量export ORACLE_SID=eapdb1# 连接并关闭实例sqlplus / as sysdbaSQL> shutdown immediate;
关闭后,再次检查 HugePages 是否已释放。
grep HugePages_Free /proc/meminfo
如果实例曾被异常终止(如 kill -9),可能会留下“僵尸”共享内存段和信号量。
# 以 oracle 用户执行ipcs -m # 检查共享内存ipcs -s # 检查信号量
如果看到多个由 oracle 用户拥有,但其关联进程已不存在的条目,则为残留资源。
使用 startup force 自动清理(推荐)
这是最安全的方法,Oracle 会自动清理与指定 SID 关联的残留 IPC 资源。
# 以 oracle 用户执行,确保 ORACLE_SID 指向崩溃的实例export ORACLE_SID=eapdb2sqlplus / as sysdbaSQL> startup force;
该命令会先执行 shutdown abort,清理资源,然后重新启动。
如果只想清理不想启动,可在启动后立即 shutdown immediate。
仅在 startup force 失效或需要编写自动化脚本时使用。
操作前必须确认没有相关的 Oracle 进程在运行。
# 以 root 或 oracle 用户执行# 自动清理所有属于 oracle 用户的共享内存段ipcs -m | grep oracle | awk '{print $2}' | xargs -n1 ipcrm shm# 自动清理所有属于 oracle 用户的信号量ipcs -s | grep oracle | awk '{print $2}' | xargs -n1 ipcrm sem
如果释放资源后仍不够,或业务要求多实例同时运行,则需要增加 HugePages 的总供应量。
# 以 root 用户执行# 假设需要增加到 130000 个echo 130000 > /proc/sys/vm/nr_hugepages
编辑 /etc/sysctl.conf 文件,修改或添加 vm.nr_hugepages 参数。
# 以 root 用户执行# 编辑文件vi /etc/sysctl.conf# 添加或修改行vm.nr_hugepages = 130000
如果 ulimit -l 显示的值小于 SGA 大小,需要调高该限制。
# 以 root 用户执行vi /etc/security/limits.conf
为 oracle 用户添加或修改 memlock 配置
建议直接设置为 unlimited,以避免未来因 SGA 调整再次遇到此问题。
# 在文件末尾添加以下两行oracle soft memlock unlimitedoracle hard memlock unlimited
limits.conf 的修改只对新的登录会话生效。
您必须退出当前的 oracle 用户会话,然后重新登录,新的 memlock 限制才会生效。
再次执行 ulimit -l 验证。
当调小 sga_target 后遇到此错误,说明 pfile 中明确设置的 SGA 组件之和大于新的 sga_target。
# 以 oracle 用户执行vi /tmp/pfile_debug.ora
找到 sga_target、sga_max_size 以及所有明确设置的 db_cache_size, shared_pool_size, large_pool_size, java_pool_size 等参数。
# ...*.sga_max_size=50176M*.sga_target=50176M#*.db_cache_size=200000M <-- 注释掉#*.shared_pool_size=40000M <-- 注释掉# ...
SQL> startup mount pfile='/tmp/pfile_debug.ora';
如果数据库使用 AMM(MEMORY_TARGET > 0),且 /dev/shm 容量不足。
# 以 root 用户执行# 假设需要扩容到 300GBmount -o remount,size=300G /dev/shm
编辑 /etc/fstab 文件,修改 tmpfs 在 /dev/shm 上的挂载配置。
# 以 root 用户执行vi /etc/fstab# 找到 /dev/shm 对应的行,修改 size 参数# 如果没有,则添加一行tmpfs /dev/shm tmpfs defaults,size=300G 00
为了使永久配置立即生效而无需重启,可以在修改 /etc/fstab 后,再执行一次临时的 remount 命令。
看完这篇,下次再遇到 ORA-27102 和 Error 28 狼狈为奸时,千万别再憨憨地去打报告申请加内存条了。
技术人的价值,往往就体现在这种“透过现象看本质”的时刻。
报错信息只是表象,内核参数才是真相。不管是 SGA 配置过大,还是 HugePages 被僵尸进程占着茅坑不拉屎,只要你掌握了文中这套“查户口”式的诊断流程,就能像做外科手术一样精准切除病灶。
希望这篇干货能成为你手中的一把利剑,帮你解决深夜排障时的燃眉之急。
如果觉得文章对你有帮助,欢迎【点赞】、【在看】或【转发】给身边的运维兄弟。
关注【安呀智数据坊】,每天一点硬核干货,带你从“救火队员”进阶为“架构专家”。
大家好,我是刘峰,安丫科技创始人 & 数据库技术高级讲师,专注于 PostgreSQL、国产数据库运维与迁移、数据库性能优化 等方向。
作为 PG中国分会官方授权讲师、PostgreSQL ACE 讲师认证专家,我长期活跃在一线项目实战中,拥有 10年以上大型数据库管理与优化经验,曾深度参与电信、金融、政务等多个行业的数据库性能调优与迁移项目。
欢迎关注我,一起深入探索数据库的无限可能,技术交流不设限!
📌 觉得有收获的话,记得点赞、收藏、转发支持一下哦,别忘了关注我获取更多数据库干货~
无论你是业务系统的技术负责人,还是数据部门的第一响应人,我们都能为你提供可靠的支持:
数据库类型支持
Oracle / MySQL / PostgreSQL / SQL Server 等主流数据库
核心服务内容
性能优化 / 故障处理 / 数据迁移 / 备份恢复 / 版本升级 / 补丁管理
系统性支持
深度巡检 / 高可用架构设计 / 应用层兼容评估 / 运维工具集成
专项能力补充
定制课程培训 / 甲方团队辅导 / 复杂问题协作排查 / 紧急救援支持
📮 如果你有一张删不掉的表、一个跑不动的查询,或者一场说不清的升级风险,欢迎来找我们聊聊。
关键词回复(可见相应文章):
oracle、mysql、pg、postgresql、sql、性能优化、故障处理、数据迁移、备份恢复、版本升级、补丁管理、深度巡检、解决方案、架构设计......
动动你的手指
给【安呀智数据坊】加个星标吧~
这样你就不会丢下我啦~