
接手离职同事的数据库,周六服务器突然重启,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 / 达梦等主流数据库的运维实战与架构分享,欢迎关注,一起做靠谱的数据库人。
欢迎赞赏支持或留言指正





