在传统行式存储中,即使只查询其中几列,Oracle 也必须把整行读进 Buffer Cache。对于分析型 SQL(大量列但仅扫少数几行),这种“拖泥带水”的方式既浪费内存带宽又拉高 CPU。 Oracle 12.1.0.2 引入 In-Memory Column Store(IM 列存储),以列为单位、纯内存、压缩、向量化扫描,专为 OLAP / 混合负载 打造。
架构速览:IM 列存储在 SGA 中的位置
区域 | 说明 |
---|---|
Buffer Cache | 传统行式缓存,事务依旧在此发生 |
Shared Pool | 保存列存储字典、表达式统计信息等 |
In-Memory Area(新 SGA 子池) | 以 INMEMORY_SIZE 初始化参数设定大小,存放列式副本 |
列副本是 只读 的,DML 仍走 Buffer Cache → 后台进程定期把更新合并到 IM 区。
优化器如何决定“要不要走列存储”
优化器成本模型在 12c 被增强,会自动识别:
- 表/分区是否
INMEMORY
- 当前会话是否允许 IM 扫描(
INMEMORY_QUERY
) - 统计信息是否“IM 感知”(
OPTIMIZER_INMEMORY_AWARE = TRUE
)
一句话:谁的成本低就选谁。IM 扫描在单列过滤、聚合场景下成本通常远低于行式全表扫 + 磁盘 I/O。
三把“开关”控制 IM 查询
初始化参数 | 作用域 | 典型用法 |
---|---|---|
INMEMORY_QUERY | SESSION / SYSTEM | 彻底关闭 IM 扫描(测试回退方案) |
OPTIMIZER_INMEMORY_AWARE | SYSTEM | 设为 FALSE 时优化器假装看不到 IM 属性 |
OPTIMIZER_FEATURES_ENABLE | SYSTEM | 若 < 12.1.0.2,等价于 OPTIMIZER_INMEMORY_AWARE=FALSE |
在 SQL 级 也可使用 Hint 做细粒度控制:
/*+ INMEMORY */
强制尝试 IM 扫描;/*+ NO_INMEMORY */
显式屏蔽。
动手实验:30 秒跑一次“内存中表扫描”
准备环境
-- 1. 给表加 In-Memory 属性(HIGH 压缩)
ALTER TABLE oe.product_information INMEMORY MEMCOMPRESS FOR QUERY HIGH;
-- 2. 手动触发一次填充(也可等待首次查询)
EXEC DBMS_INMEMORY.REPOPULATE('OE','PRODUCT_INFORMATION');
执行查询
SELECT *
FROM oe.product_information
WHERE list_price > 10
ORDER BY product_id;
查看执行计划
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
输出关键片段:
--------------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes |TempSpc|Cost(%CPU)|Time|
--------------------------------------------------------------------------------------------
|*2| TABLE ACCESS INMEMORY FULL| PRODUCT_INFORMATION | 285| 62415| | 5 (0)|00:00:01|
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - inmemory("LIST_PRICE">10)
filter("LIST_PRICE">10)
INMEMORY FULL
证明走了 IM 列存储;inmemory(...)
表示列式谓词评估;- Cost 从 21 降到 5,扫描时间 < 1 秒。
常见问题
- Hint 无效? 确认对象已
INMEMORY
,并检查INMEMORY_QUERY = ENABLE
。 - 为什么还是走全表扫? 可能是列存储尚未填充完,查
V$IM_SEGMENTS.POPULATE_STATUS
。 - DML 会变慢吗? IM 区只读,DML 仍走 Buffer Cache;大量更新时需关注后台
IMCO
合并负载。 - 列太多内存不够? 可以只把热列设为
INMEMORY
:ALTER TABLE t INMEMORY (col1, col2) NO INMEMORY (col3);
- IM 列存储 = 内存 + 列式 + 压缩 + 向量化扫描
- 优化器基于成本自动决策,亦可 Hint 强制/禁用
- 通过
INMEMORY_*
参数与 Hint 就能完成灰度测试或性能回退 - 典型收益:分析型 SQL 10× 甚至 100× 提速