news 2026/9/11 2:18:02

SGA与Swap的暗战:Oracle内存管理与性能优化实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SGA与Swap的暗战:Oracle内存管理与性能优化实战

拿到这个标题,我第一反应是想到那些深更半夜被电话叫起来处理数据库性能问题的场景。系统突然变慢,登录服务器一看,free -g里 swap 的使用率涨得离谱,而 Oracle 的 SGA 占用却高得吓人。很多人第一反应是“内存不够”,然后下意识去加内存、扩 swap,结果问题依然反复出现。这个标题说小了是内存管理,说大了其实是理解 Oracle 与操作系统之间如何协作的根本问题。如果你负责过任何一套生产环境,迟早会撞上 SGA 和 swap 之间的这场“暗战”。

这篇文章不聊虚的,我会直接从内存分配机制讲起,说明为什么 SGA 配置不当会触发 swap 的异常使用,然后给出一套完整的诊断命令和优化方案。内容适合数据库管理员、系统运维以及刚接触 Oracle 调优的开发者,数据量大、内存吃紧的环境尤其值得花十分钟看完。

1. 先搞清楚 SGA 与 swap 在系统里的真实角色

1.1 SGA 不只是“一块大内存”

很多人一说 SGA 就想到SGA_MAX_SIZE,好像把参数调大就万事大吉。实际上,SGA 是一个组合体,由多个独立的内存组件构成,常见的包括:

  • DB_CACHE_SIZE,缓存数据块的默认缓冲池,也是 SGA 里最吃内存的部分;
  • SHARED_POOL_SIZE,存放 SQL 游标、执行计划、数据字典缓存;
  • REDO_LOG_BUFFER,重做日志的缓冲,写事务日志时先经过这里;
  • JAVA_POOL_SIZESTREAMS_POOL_SIZELARGE_POOL_SIZE等,用于特定功能模块。

从 Oracle 10g 开始引入了自动内存管理(AMM,即MEMORY_TARGET)和自动共享内存管理(ASMM,即SGA_TARGET),但这不代表我们可以完全放手不管。内核在分配 SGA 时,通常会以 mmap 的方式映射到物理内存,并且为了效率会尽量常驻。问题恰恰出在这里:SGA 的设计初衷是“常驻物理内存”,但操作系统并不一定买账。

1.2 swap 的真正含义:内存的“溢出区”

swap 是操作系统把暂时不用的内存页挪到磁盘的一块交换空间。它不是 Oracle 专用的东西,而是内核在物理内存压力下的一种“安全阀”。很多 DBA 对 swap 有误解,觉得 swap 越大越好,或者认为只要 oracle 进程用了 swap 就一定要消灭它。实际上 swap 的使用分两种情况:

  • 一种是系统内存真正不足,大量进程的页面被换出,这是性能灾难;
  • 另一种是系统在空闲时主动把部分很少访问的匿名页面换出去,这是内核的正常行为,特别是在vm.swappiness设置较高的时候。

Oracle 的 SGA 被换出属于第二种情况的变种——本应常驻的页面被内核判定为“低热度”,继而写入了 swap,但 SGA 中的缓存一旦被换出,再访问时就触发换入(swap in)。这个换入换出的过程就是性能“杀手”,它比磁盘多块读的代价还要高得多。

1.3 SGA 与 swap 产生联系的本质

SGA 里的 buffer cache 被访问的频率极高,如果这部分内存页被换到 swap,意味着每个数据块访问都伴随一次磁盘 I/O,数据库能不慢吗?而之所以会发生换入换出,通常是因为两个原因叠加:SGA 设置过大,导致操作系统物理内存余量不足,内核被迫把 SGA 的页面当作回收对象;或者是服务器上还有其他进程(比如 PGA、应用服务、监听进程)抢内存,系统整体处于“紧平衡”,一旦有突发内存申请,就开始动 swap。

搞清楚这个本质之后,我们才能谈优化。否则就是无头苍蝇。

2. 为什么 SGA 设置会牵动 swap 的敏感神经

2.1 物理内存分配与页面回收机制

先看一个基础模型。Linux 内核在分配内存时会经历几个层次:

  1. 优先从空闲页列表分配;
  2. 空闲不足时开始回收可回收页,包括文件页缓冲(page cache)和可换出的匿名页;
  3. 如果回收仍然不够,会触发 OOM 或者进行交换。

Oracle SGA 通过shmgetmmap申请内存,这些页面属于匿名页,不是文件页。文件页回收代价低,脏页写回磁盘即可,而匿名页如果被回收,必须先写入 swap,所以内核一旦回收了 SGA 所在的匿名页,swap 使用率就会上升。

有一个细节很多人忽略:Linux 内核的回收代码是很“机械”的,它不会知道某个页面属于 Oracle SGA,也不会知道这个页面可能即将被高频访问。它只看一个指标:这个页面最近是否被访问过。如果 SGA 启动后长时间没有特定 region 的访问,内核可能误判为“冷页面”而换出。这就是为什么有些系统明明内存没耗尽,swap 里却出现了 oracle 进程的页面。

2.2vm.swappiness参数的双刃剑

vm.swappiness的默认值是 60,含义是内核倾向于回收匿名页的程度。值越大,内核越积极地把匿名页换出;值越小,内核越倾向于回收文件页缓存而不是动 swap。

对于 Oracle 环境,很多最佳实践建议把 swappiness 设置为 1 或 0,但这有个陷阱:如果完整设置为 0,在内存极端紧张时,内核可能优先回收 page cache,导致磁盘读性能波动,甚至影响文件系统元数据操作。新版本内核(如 5.x)建议最小值设为 1,而不建议设 0。设置方法是:

sysctl -w vm.swappiness=1 echo "vm.swappiness = 1" >> /etc/sysctl.conf

需要注意的是,此参数控制的是“倾向性”,不是绝对开关。就算设为 1,如果物理内存真不够,该用 swap 还是得用。

2.3 SGA 设置过大导致的内存黑洞

很多系统是从物理机迁移到虚拟机上的,内存参数还是沿用旧配置。比如原来物理机 256G,SGA 设了 192G,后来迁到 128G 的虚机上,DBA 忘了调参。结果是系统启动后 free 立刻见底,内核只能疯狂使用 swap,最后整库卡死。这种情况我见过太多次,不是说 SGA 不能大,但必须建立在“物理内存足够”的前提下。

2.4 SGA 过小的隐患也指向 swap

SGA 设得小,buffer cache 装不下工作集,业务会频繁进行物理读。物理读超过一定阈值,磁盘 I/O 队列拉高,用户事务被阻塞,最终也会导致会话堆积,内存消耗飙升,系统压力间接反映到 swap 使用率上。所以这个关系不是线性的“SGA 大 -> swap 高”,而是要找到一个平衡点。

3. 实战诊断:如何判断 swap 升高是否由 SGA 引发

3.1 三步定位法

面对 swap 升高,我一般按照三个步骤定位:

第一,确认操作系统整体内存水位。用free -g看 total、used、free、available,重点看 available 而不只是 free,因为 Linux 的 free 列并不包含可回收的缓存,available 才是应用可用内存的估算值。

第二,确认 swap 使用趋势。用vmstat 2 10观察 si(swap in)和 so(swap out)列。如果 so 列持续大于 0,说明系统正在主动换出页面,这非常危险;如果只是 si 偶尔出现,可能只是瞬时抖动。

第三步,细化到进程级别。用top -Hp <oracle_pid>或者pidstat -r -p <pid> 2去查看 oracle 进程的 RSS,对比其虚拟内存 VSZ。如果某个 oracle 进程的 RSS 远小于 VSZ,极有可能有大量页面被换出。

实际上我们可以写一个简单命令把占用 swap 最多的进程找出来:

for f in /proc/*/status; do awk '/VmSwap/{sw[$1]=$2} END{for (k in sw) print k, sw[k]}' $f 2>/dev/null; done | sort -k2 -rn | head -20

这个命令遍历每个进程的 VmSwap 字段,把 swap 占用最高的进程列出来。Linux 的/proc/pid/status中 VmSwap 表示该进程使用了多少 swap,这个信息对定位“是不是 oracle 吃了 swap”非常直接。

3.2 Oracle 侧的视图:v$sgastat 和 v$pgastat

在 Oracle 内部,我们可以借助视图感知内存组成:

select name, bytes/1024/1024 as size_mb from v$sgastat where pool is not null order by bytes desc;

再看 PGA 的情况:

select name, value/1024/1024 as value_mb from v$pgastat where name in ('total PGA allocated','maximum PGA allocated','total PGA inuse');

如果 PGA 持续增长且占用极高,要小心排序、哈希连接等操作耗尽 PGA,导致物理内存不够,进而诱发 swap。

3.3 用 sar 保留历史证据

线上问题排查最怕没有历史数据。如果你提前开启了sysstat,那么可以直接用sar -r回顾内存水位:

sar -r -f /var/log/sa/sa$(date +%d -d yesterday) | grep -i swap

kbswpusedkbswpfree两列会告诉你前一天 swap 占用情况。配合sar -B查看 pgpgin/pgpgout,可以判断是否存在大量的页面换入换出。这套东西在没有监控平台的场景下就是救命稻草。

4. 从根上优化:让 SGA 不再“赖”在 swap 身上

4.1 确定 SGA 的合理上限

SGA 大小的设置没有万能公式,但有个经过验证的静态参考:单独一台数据库服务器上,SGA + PGA 的总和通常建议控制在物理内存的 50% 到 75% 之间;如果操作系统还需要运行大量其他服务,这个比例还要更低。

为什么不能直接给到 90%?因为 Linux 的文件系统缓存对 Oracle 也有正向作用。redo 文件的写入、数据文件的读入都需要经过 OS 缓存层,完全挤压掉 page cache 会让数据库的物理 I/O 更慢。我一般建议数据库服务器上 SGA 最大值不超过物理内存的 70%,且要为 PGA 预留同等大小的空间。看过太多 SGA 占 80% 然后 PGA 爆掉导致内存互换的案例了。

实操中,可以设置一个初始值,然后用自动内存管理来自动调整,比如:

alter system set sga_max_size=96G scope=spfile; alter system set sga_target=96G scope=spfile;

但要注意,sga_max_size是硬上限,sga_target是动态调整目标。如果物理内存只有 128G,目标也没必要设 96G。

4.2 启用 HugePages:规避 swap 的关键一招

Linux 默认的内存页大小为 4KB,管理一个 96G 的 SGA 需要约 2400 万个页表项,TLB 根本覆盖不过来;同时那些被换出的页也格外分散,内核回收时开销巨大。启用大页(通常 2MB)后,页表项数量骤减,TLB 命中率大幅提高,SGA 区域几乎不会成为操作系统的回收候选。

配置大页的步骤不复杂,但每步都容易出错:

  1. 查看当前大页配置:
grep HugePages_Total /proc/meminfo grep Hugepagesize /proc/meminfo
  1. 修改/etc/sysctl.conf,预留大页数量。假设想让 64G 的 SGA 全部落在大页上,计算方式是 64GB / 2MB = 32768 个页面,再加上额外余量 100 个页面左右:
vm.nr_hugepages = 32868 vm.hugetlb_shm_group = 54321 # 替换为 dba 组的 gid
  1. 应用或重启生效:
sysctl -p
  1. 把数据库锁在大页上,需要将memory_targetmemory_max_target设为 0,避免 Oracle 使用共享内存文件系统而绕过 HugePages。同时在 Oracle 11.2.0.3 及以上版本,可以启用参数:
alter system set use_large_pages=only scope=spfile;

use_large_pages=only的意思很明确:只允许使用大页,如果没有足够大页,实例直接启动失败而不是偷偷退回普通内存,这能帮我们尽早发现问题。

4.3 调整lock_sga:把 SGA 钉死在物理内存

另一个能彻底解决 SGA 换出问题的参数是LOCK_SGA,它在 Unix/Linux 平台上会将整个 SGA 锁定在物理内存中。但要注意,这个参数通常与 HugePages 搭配使用,且操作系统必须允许相关用户锁定足够的内存。修改limits.conf里的memlock,然后重启实例。

oracle soft memlock unlimited oracle hard memlock unlimited

如果启用了 HugePages 并把lock_sga=true,那么 Oracle 的共享内存基本不可能再被 swap,这是最“稳”的组合。

4.4 通过调整数据库缓存块大小优化局部性

除了直接在 OS 层下手,数据库层也有辅助手段。如果业务数据有明显的热点集,可以用db_keep_cache_size设置一个独立的 keep 池,把高频访问的表放到里面,并用alter table ... storage(buffer_pool keep)指定。这样 BUFFER_CACHE 主池的压力会减小,SGA 总体占用更可控,内存“热区”更集中,被换出的概率自然降低。

4.5 别忘记 PGA 的调节

PGA 是另一个内存大户。排序区、哈希区都从 PGA 分配,如果很多会话同时做大的排序操作,PGA 会瞬间暴涨,把物理内存吃掉一大块。可以用PGA_AGGREGATE_TARGET限制总量,并监控v$pgastatover allocation count是否反复出现。如果经常出现 over allocation,说明 PGA 限制过小或工作负载过大,导致额外分配临时段,间接引发内存和 I/O 压力,最终也会反映到 swap。

5. 常见问题与排错实录

5.1 现象:swap 使用率稳定增长但 si/so 很低

这种情况最迷惑人。系统并不在进行大量换页,但 swap 的 used 一直居高不下,比如占了 20G。这通常意味着之前某个时段发生过内存压力,部分页面被换出后一直没有被再次访问,所以 si/so 降下来了,但 swap 占用不会自动归还。

排查方向:看/proc/meminfoSwapCached是否偏高。如果SwapCached高,说明 swap 中有页面被读回内存但尚未释放;可以用swapoff -a && swapon -a来清空 swap,但生产环境使用此命令要极其小心,会导致所有进程再次换入,内存水位瞬时飙升。稳妥做法是先调低 swap 使用,重启数据库实例或逐步回收。

5.2 现象:启动数据库后操作系统内存迅速耗尽

经常遇到有朋友说“数据库一启动,free 就剩几个 G”。如果 SGA 已经占了 70% 物理内存,而数据库服务器上还跑着 agent、监控、备份脚本等,free 低是必然的。这里要提醒一句,Linux 的 free 参数中available列比free列更有参考意义。只要 available 大于 20%,系统大概率还能兜住,不必过度紧张。但是如果 available 长期低于 10%,就需要考虑收缩 SGA 或迁走非核心进程。

5.3 现象:设置了sga_max_size但物理内存没被完全利用

多数情况下 SGA 是按需触碰的,即只有访问过的内存页才会实际驻留在物理内存中。因此sga_max_size设了 96G,并不代表开机就吃掉 96G 物理内存。有人看到这个现象误以为“参数没生效”,其实是正常行为。要验证 SGA 是否在物理内存里常驻,可以大量扫描数据后再次观察内存占用。

5.4 场景速查表

为了方便直接抄作业,我整理一张排查表供参考:

现象可能原因优先检查处理思路
swap 使用率突增,数据库变慢SGA 过大或工作负载瞬间抬升vmstat 的 so 列,top 的 oracle 进程 VmSwap调整 SGA,启用 HugePages,必要时重启实例
swap 使用率不高但 si/so 频繁内存紧张或回收策略激进swappinesspgpgin/pgpgout调低 swappiness,检查 page cache 是否被过度回收
数据库进程 RSS 持续增长PGA 被大量消耗v$pgastat、物理内存 available限制 PGA,优化 SQL,减少大规模排序
启动后 free 极少但系统不卡大页未启用或 SGA 大面积触碰内存/proc/meminfo的 HugePages_Total启用 HugePages 减少页表开销
多个实例共存时某实例特别卡共享内存分配不均各实例 sga_target、物理内存拓扑按实例业务量分配,避免挤占

5.5 独家避坑经验:别把 swap 直接关闭

很多人觉得 Oracle 用了 swap 就是不行,于是干脆swapoff -a,把 swap 彻底关掉。这条操作在低负载环境可能没事,但在内存尖峰来临时,内核没有缓冲,直接触发 OOM Killer,可能杀掉 oracle 进程。相比进程被杀,swap 抖动反而是可以接受的。正确的做法是保留一个几 G 的 swap 分区作为最后的兜底,同时用 HugePages + 合理 SGA 让日常几乎碰不到 swap。

5.6 一个实际调优案例的复盘

这里分享一个简化版的实际案例,方便理解整体流程。一台 64G 物理内存的数据库服务器,SGA 设为 40G,PGA 默认 4G,vm.swappiness保持默认 60。业务高峰期时 swap 使用率升至 8G,数据库大量 buffer busy wait 和 log file sync。

我当时的排查顺序是:先free -g发现 available 只剩下 3G,再用pidstat -r看到 oracle 进程 VmSwap 约有 5G,确认 SGA 页面确实被换出。然后检查v$sgastat,发现 buffer cache 的尺寸并没有问题,真正原因是 swappiness 太高导致内核把共享内存页当普通匿名页换出了。

处理方式分两步:第一步临时调低vm.swappiness=1,并用echo 3 > /proc/sys/vm/drop_caches清理文件缓存,缓解内存压力;第二步启用 HugePages,给数据库的 SGA 分配大页内存,然后修改use_large_pages=only重启实例。调整后 swap 使用率回落到接近 0,高峰期的 log file sync 不再出现。

这个案例想说明的是,SGA 与 swap 的问题往往不是单点参数错误,而是 OS 层和数据库层多个配置互相叠加的结果。遇到问题别急着加内存,先逐层排查,理清因果再动手。

6. 日常运维中值得坚持的几个习惯

6.1 建立内存和 swap 的基线监控

我不建议只用一套固定阈值去报警,更好的做法是连续收集两周的内存和 swap 数据,形成基线。比如某套系统平时 swap 使用率一直在 2G 以内,某天突然到 6G,即便绝对值不高也是预警信号。用 Prometheus、Zabbix 或者简单的 cron 脚本都行,关键是数据要沉淀下来。

6.2 升级或变更前先做内存评审

在数据库实例参数变更、SGA 调整、服务器迁移之前,先做一次简单的计算:

  • 确认物理内存总量,检查是否符合 SGA + PGA + OS + 其他进程的需求;
  • 验证 HugePages 数量和 SGA 的匹配度;
  • 测试 swap 空间是否足够支撑极端场景;
  • 观察变更后的vmstat/proc/meminfo是否发生异常。

这套“变更前评审”成本不高,却能省下一堆半夜故障。

6.3 定期检查大页利用率

大页配了不代表一直在用。Oracle 12c 之后可以用lsnrctl statusgrep HugePages等方式确认实例是否真正使用了大页。有些版本或迁移场景下,sga 使用了大页但参数配置不全,导致大页利用率只有一半。配合监控HugePages_RsvdHugePages_Free的变化,就能及时发现配置漂移。

7. 我个人在实际操作中的一些体会

调优做多了,你会发现 SGA 与 swap 的问题,本质上是“内存资源归一化分配”的问题。数据库认为 SGA 是它的私有领地,操作系统却把它看作可以回收的匿名页,两者之间的错位,就是故障的来源。每次处理类似问题,我都提醒团队:先把系统层面的数据看全,再动数据库参数,千万不能拿着“经验值”硬套。

最后分享一个小技巧:如果你不确定当前服务器的内存分配是否合理,可以在业务低峰期手动触发一次内存回收测试。先记录 swap 使用,再使用echo 1 > /proc/sys/vm/drop_caches,随后观察数据库性能尖刺和 swap 变化。这比在高峰期排查问题要安全得多。也提醒一句,这个操作不要在业务高峰期做,否则缓存的强制回收反而会有一点性能回调。这个“小动作”能帮你快速判断系统对内存回收的敏感度,长期来看非常有用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 2:15:34

2026 Kali Linux 安装教程:虚拟机部署与初始化配置全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 2:13:56

基于lwIP的应用层协议仿真实战:HTTP/MQTT/CoAP/DNS报文详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 2:13:31

企业软件数据安全全景复盘:内部泄露、越权查看、恶意导出、数据误删,90%企业都裸奔运行

提到企业数据安全&#xff0c;绝大多数老板和运维的第一认知是&#xff1a;防黑客、防外网入侵、防服务器被攻破。于是企业花成本做防火墙、做服务器防护、做域名安全、做外网拦截。但真实行业数据极其扎心&#xff1a;企业95%以上的数据泄露、数据丢失、数据倒卖&#xff0c;全…

作者头像 李华
网站建设 2026/9/11 2:11:59

从无标题项目到产品原型的创意管理实践

1. 项目概述作为一名从业多年的技术博主&#xff0c;我经常遇到一个困扰&#xff1a;当灵感来临时&#xff0c;脑海中会突然蹦出一些零散但极具潜力的项目想法&#xff0c;却因为缺乏系统记录和后续开发而最终流失。这种情况在创意工作者和技术开发者中非常普遍——我们称之为&…

作者头像 李华
网站建设 2026/9/11 2:11:26

Python变量与数据类型详解:从基础到实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华