暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

Oracle 19c In-Memory 核心功能详解:一次"内存不足"告警的排查实录

原创 布衣 2026-06-07
328

image.png

接手离职同事的数据库,周六服务器突然重启,alert 日志开始每 6 分钟报一次错——本文完整复盘这次 Oracle 19c In-Memory 参数配置不当引发的故障,并系统讲解 INMEMORY_SIZE 的功能、用途与注意事项。


一、故障现象:每 6 分钟准时报错

周六上午,接手不久的一套 Oracle 19c 生产库突然出现规律性告警。查看 alert 日志,发现一个非常规律的现象:

2026-06-07T07:37:04.120153+08:00
Insufficient memory to populate table to inmemory area
2026-06-07T07:43:07.119658+08:00
Insufficient memory to populate table to inmemory area
2026-06-07T07:49:10.100112+08:00
Insufficient memory to populate table to inmemory area

每约 6 分钟报一次,报错内容完全一致:Insufficient memory to populate table to inmemory area

同时,系统日志显示服务器在 21:31 发生了重启:

Jun  6 21:31:03 TWOdb_19c kernel: Linux version 3.10.0-1127.19.1.el7.x86_64

这不是普通的性能问题,而是一个典型的 Oracle In-Memory 配置不当导致的持续性故障


二、Oracle In-Memory 是什么?

2.1 核心概念(通俗版)

Oracle In-Memory(简称 IM)是 Oracle 12c R1 引入的一个双格式内存数据库引擎

传统 Oracle 数据在内存中是以行格式(buffer cache)存储的,适合 OLTP 增删改;而 IM 把数据额外以列格式存放在一块专门的内存区域里,适合 OLAP 分析查询。

传统 Oracle 内存布局:
┌─────────────────────────────────────────────────────┐
│  SGA                                                │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────┐   │
│  │ Buffer Cache│  │ Shared Pool  │  │ 其他...   │   │
│  │ (行格式存储) │  │              │  │           │  │
│  └─────────────┘  └──────────────┘  └───────────┘  │
└────────────────────────────────────────────────────┘

启用 In-Memory 后:
┌─────────────────────────────────────────────────────────┐
│  SGA                                                    │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ Buffer Cache│  │ Shared Pool  │  │ In-Memory    │    │
│  │ (行格式)     │  │              │  │ Area (列格式)│    │
│  └─────────────┘  └──────────────┘  └──────────────┘    │
└─────────────────────────────────────────────────────────┘

关键优势:同一份数据,行格式和列格式同时存在,OLTP 和 OLAP 查询各自走最优路径,互不影响。

2.2 INMEMORY_SIZE 参数的作用

INMEMORY_SIZE 是控制 IM 列存储区域的核心参数

参数 说明
INMEMORY_SIZE IM 列存储区域的大小,单位是字节
最小值 100MB(19c)
设为 0 关闭 In-Memory 功能
静态参数 修改后需要重启数据库生效
-- 查看当前设置 SHOW PARAMETER INMEMORY_SIZE; -- 设置为 10GB(需要重启) ALTER SYSTEM SET INMEMORY_SIZE = 10G SCOPE=SPFILE;

2.3 哪些对象可以放入 IM 区域?

通过 INMEMORY 属性控制,可以对表级别精细控制:

-- 整表启用 IM ALTER TABLE schema_name.table_name INMEMORY; -- 指定列启用 IM(仅加载部分列,节省内存) ALTER TABLE schema_name.table_name INMEMORY(col1, col2, col3); -- 取消 IM ALTER TABLE schema_name.table_name NO INMEMORY; -- 设置压缩方式 ALTER TABLE schema_name.table_name INMEMORY MEMCOMPRESS FOR QUERY LOW;

2.4 In-Memory 的适用场景

适合 不适合
大型事实表的聚合分析查询 纯 OLTP 业务(只有简单增删改查)
扫描大量行的 SELECT 语句 表数据量很小(全表扫描本身很快)
报表类、统计分析类应用 内存资源紧张的环境
宽表(列数很多)的少数列查询 写入密集型应用

三、深入排查:数据说话

拿到报错后,直接用 SQL 排查,数据如下。

3.1 查看 IM 相关参数

SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME LIKE '%inmemory%' OR NAME LIKE '%sga%';

关键发现

参数 解读
INMEMORY_SIZE 1073741824(1GB) IM 区域只有 1GB
SGA_TARGET 21474836480(20GB) SGA 总大小 20GB
SGA_MAX_SIZE 21474836480(20GB) SGA 最大 20GB

问题直接暴露INMEMORY_SIZE = 1GB,而 SGA 总共只有 20GB,IM 区域占比仅 5%,这个配置本身就非常可疑。

3.2 查看哪些表标记了 INMEMORY

SELECT OWNER, TABLE_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION FROM DBA_TABLES WHERE INMEMORY = 'ENABLED';

结果出人意料

OWNER TABLE_NAME PRIORITY COMPRESSION
TWO T_TRANS LOW FOR QUERY LOW

整个数据库,只有一张表标记了 INMEMORY——而且是一张历史交易表。

3.3 这张表有多大?

SELECT SUM(BYTES)/1024/1024/1024 AS TOTAL_GB FROM DBA_SEGMENTS WHERE OWNER = 'TWO' AND SEGMENT_NAME = 'T_TRANS';

结果:这张表约 95GB

3.4 IM 区域的实际状态

SELECT * FROM V$INMEMORY_AREA;
POOL ALLOC_BYTES USED_BYTES POPULATE_STATUS
1MB POOL 737,148,928 (~703MB) 737,148,928 OUT OF MEMORY
64KB POOL 318,767,104 (~304MB) 33,161,216 DONE

1MB POOL 已完全耗尽,状态 OUT OF MEMORY,印证了 alert 日志的报错。

SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, INMEMORY_SIZE/1024/1024 AS MB_LOADED, BYTES_NOT_POPULATED/1024/1024 AS MB_NOT_LOADED FROM V$IM_SEGMENTS;
SEGMENT_NAME POPULATE_STATUS MB_LOADED MB_NOT_LOADED
T_TRANS OUT OF MEMORY 734.625 MB 32,615 MB (~31.8GB)

结论非常清晰:Oracle 试图把这张 95GB 的表加载进仅 1GB 的 IM 区域,加载了 734MB 后 1MB POOL 耗尽,剩余约 31GB 数据永远无法加载,MMON 后台进程每 6 分钟重试一次,无限循环。


四、为什么是每 6 分钟?

MMON(Manageability Monitor)是 Oracle 的后台管理进程,负责:

  • AWR 快照的自动采集
  • 自动 In-Memory 管理(IM 自动加载/驱逐)
  • 空间管理建议任务的运行

MMON 对 IM 区域有定期检测机制,约每 6 分钟触发一次 populate 重试。这就是为什么 alert 日志中的报错精确地呈现 6 分钟周期。

时间线:
18:20  首次检测到 IM populate 失败 → 写入 alert.log
18:26  MMON 第一次重试 → 失败 → 写入 alert.log
18:32  MMON 第二次重试 → 失败 → 写入 alert.log
...(每 6 分钟重复)
21:20  系统层面发生重启(可能 OOM 或 DBA 手动干预)
21:31  数据库启动完成
...(6 分钟周期继续)

五、解决方案

方案一:取消表的 INMEMORY 属性(推荐,无需重启)

这是代价最小的方案,立即生效,不需要重启数据库:

-- 立即取消 T_TRANS 的 IM 属性 ALTER TABLE TWO.T_TRANS NO INMEMORY; -- 确认无其他 IM 表 SELECT OWNER, TABLE_NAME FROM DBA_TABLES WHERE INMEMORY = 'ENABLED'; -- 确认 IM 区域释放 SELECT * FROM V$INMEMORY_AREA;

执行后,最多 6 分钟,下一个 MMON 周期检测到无需加载,alert 日志中的周期性报错即停止。

方案二:关闭 In-Memory 功能(彻底,需重启)

如果确认业务上根本没有使用 IM 特性的需求(没有 OLAP 分析场景),最干净的做法是直接关闭:

-- 先取消所有表的 IM 属性 ALTER TABLE TWO.T_TRANS NO INMEMORY; -- 将 INMEMORY_SIZE 设为 0(关闭 IM) ALTER SYSTEM SET INMEMORY_SIZE = 0 SCOPE=SPFILE; -- 重启数据库生效 SHUTDOWN IMMEDIATE; STARTUP; -- 验证 SHOW PARAMETER INMEMORY_SIZE;

方案三:扩大 INMEMORY_SIZE(需评估硬件资源)

如果确实需要使用 IM 特性,必须将 INMEMORY_SIZE 扩大到能容纳目标表的大小:

-- 计算需要的内存(考虑压缩比,通常 1.2 倍) -- T_TRANS 约 95GB,建议设置 120GB 以上 ALTER SYSTEM SET SGA_TARGET = 130G SCOPE=SPFILE; ALTER SYSTEM SET SGA_MAX_SIZE = 130G SCOPE=SPFILE; ALTER SYSTEM SET INMEMORY_SIZE = 120G SCOPE=SPFILE; SHUTDOWN IMMEDIATE; STARTUP;

⚠️ 重要提示:根据系统日志,该服务器的物理内存仅约 64GB(HP DL580 G7,2012 年设备),远不足以支持 120GB 的 IM 区域。此方案在当前硬件上不可行。


六、In-Memory 使用注意事项(避坑指南)

通过本次故障,总结几条关键注意事项:

6.1 启用前必须评估内存需求

最常见的错误:给大表设置了 INMEMORY 属性,但 INMEMORY_SIZE 设置得太小。

-- 启用 IM 前的标准评估流程 -- Step 1: 计算目标表的总大小 SELECT OWNER, SEGMENT_NAME, SUM(BYTES)/1024/1024/1024 AS TOTAL_GB FROM DBA_SEGMENTS WHERE OWNER = 'xxx' GROUP BY OWNER, SEGMENT_NAME; -- Step 2: 评估压缩后的大小(通常 2:1 ~ 4:1) -- 实际加载后在 V$IM_SEGMENTS.INMEMORY_SIZE 中查看 -- Step 3: INMEMORY_SIZE 至少为压缩后大小的 1.2 倍

6.2 优先使用优先级控制加载顺序

当 IM 区域不足以容纳所有 IM 表时,用 PRIORITY 控制加载优先级:

-- 高优先级:优先加载 ALTER TABLE important_table INMEMORY PRIORITY HIGH; -- 低优先级:区域不足时不强制加载 ALTER TABLE large_table INMEMORY PRIORITY LOW; -- NONE:不自动加载,需要手动触发 ALTER TABLE manual_table INMEMORY PRIORITY NONE;

6.3 仅加载需要的列

95GB 的表,如果分析查询只用其中 3 列,完全没必要全表加载:

-- 仅对指定列启用 IM,大幅减少内存占用 ALTER TABLE TWO.T_TRANS INMEMORY(barcode_id, trans_date, amount);

6.4 生产环境变更前务必测试

本次故障的根源很可能是离职同事测试 IM 功能后未清理,直接将测试配置遗留到了生产环境。

规范做法

-- 1. 测试环境验证 IM 效果 -- 2. 确认查询确实走了 IM(执行计划中有 TABLE ACCESS INMEMORY) EXPLAIN PLAN FOR SELECT ...; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- 3. 评估内存收益比,确认值得 -- 4. 生产变更需在维护窗口执行,并准备回退方案

6.5 监控 IM 区域使用情况

将以下查询加入日常监控脚本:

-- 每日监控:IM 区域使用率 SELECT ROUND(USED_BYTES / ALLOC_BYTES * 100, 2) AS USED_PCT, ALLOC_BYTES/1024/1024/1024 AS ALLOC_GB, USED_BYTES/1024/1024/1024 AS USED_GB FROM V$INMEMORY_AREA WHERE POOL = '1MB POOL'; -- 每日监控:是否有加载失败的 segment SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, ROUND(BYTES_NOT_POPULATED/1024/1024/1024, 2) AS GB_NOT_LOADED FROM V$IM_SEGMENTS WHERE POPULATE_STATUS != 'COMPLETED';

七、总结

项目 内容
故障现象 alert 日志每 6 分钟报 Insufficient memory to populate table
根因 INMEMORY_SIZE=1GB,但 TWO.T_TRANS 表约 95GB,IM 区域严重不足
触发机制 MMON 后台进程每 6 分钟尝试重新 populate,持续失败
解决方案 ALTER TABLE ... NO INMEMORY(立即生效);或设置 INMEMORY_SIZE=0 后重启(彻底关闭)
预防建议 启用 IM 前评估内存需求;测试环境验证;生产变更走流程;日常监控 IM 区域使用率

核心教训:In-Memory 是一个强大的特性,但不是"开了就完事"的功能。它需要认真的容量规划、精细的对象选择,以及持续的监控。把 95GB 的表放进 1GB 的 IM 区域,就像试图把一头大象塞进冰箱——不是大象的问题,是冰箱选小了。


专注于 Oracle / MySQL / PostgreSQL / 达梦等主流数据库的运维实战与架构分享,欢迎关注,一起做靠谱的数据库人。

欢迎赞赏支持或留言指正
image.png

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论