Oracle「统计信息查询转换」深度解析

想象一个 8,100 万行的 SALES 表,执行下面这条语句需要多久?

SELECT MAX(amt), MAX(volume) FROM sales;

在常规认知里,再快的存储也得扫几秒。但如果在执行计划里看到前缀为 VW_SQT_% 的视图,恭喜你——Oracle 刚刚用「统计信息查询转换(Statistics-Based Query Transformation)」把 8,100 万行的大表变成了一次数据字典里的小查询,耗时从秒级降到毫秒级。

本文将拆解这项技术的工作机制、使用场景、踩坑点以及落地建议。


技术原理:把表换成元数据

什么是统计信息查询转换?

Oracle 优化器在解析阶段发现:

  • 聚合函数只需 最小值、最大值、行数、NDV 等统计量;
  • 相关列的统计信息 最新且可信
  • 查询模式 不在事务内部

此时,优化器把原始查询重写为对数据字典(SYS.HIST_HEAD$SYS.TAB_STATS$ 等)的查询,不再访问基表。执行计划中会出现形如 VW_SQT_ 的视图,它是内部生成的临时视图,用来承载统计结果。

统计信息里到底有什么?

统计项来源视图示例值
行数TAB_STATS$81,000,000
最小值HIST_HEAD$1.23
最大值HIST_HEAD$99,999.88
NDVHIST_HEAD$2,147,483

只要这些值满足查询语义,优化器就能直接返回结果。


触发条件:不是所有 MAX() 都能走捷径

维度允许转换不允许转换
聚合函数MIN, MAX, COUNT(*), APPROX_COUNT_DISTINCTSUM, AVG, LISTAGG, 窗口函数
统计准确性100% 采样、无 DML 后未收集、非人工 SET_STATS抽样统计、导入统计信息、统计信息过期
查询类型无 WHERE,或 WHERE 不影响统计列有列上的范围谓词、多列组合过滤
事务状态非事务内(autocommit 或只读事务)当前会话有未提交 DML
并行/串行并行或串行均可与并行度无关
对象类型普通表、分区表外部表、临时表、IOT 表

案例复盘:8,100 万行表的 1 毫秒 MAX()

-- 收集最新统计信息(100% 采样)
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
    ownname     => 'SH',
    tabname     => 'SALES',
    estimate_percent => 100,
    method_opt  => 'FOR ALL COLUMNS SIZE 1',
     cascade     => TRUE);
END;
/

-- 查询
SELECT MAX(amt), MAX(volume) FROM sales;

执行计划

--------------------------------------------------------------------------------
| Id | Operation               | Name           | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |                 |     |       |     2 (100)|
|   1 | VIEW                   | VW_SQT_65BBF4BE |   1 |   26 |     2   (0)|
|   2 |   FAST DUAL             |                 |   1 |       |     2   (0)|
--------------------------------------------------------------------------------

关键点:

  • VW_SQT_65BBF4BE 出现,说明发生了转换;
  • 基表 SALES 未出现,彻底避免了 8,100 万行扫描;
  • Cost 从 624 降到 2,执行时间从 3.4 s 降到 0.001 s。

踩坑指南:统计信息失效的 4 大场景

场景现象解决方案
抽样统计执行计划仍走全表改用 estimate_percent => 100AUTO_SAMPLE_SIZE
DML 后未收集统计信息 STALE 标记设置 DBMS_STATS.AUTO_TASK 或手动 GATHER_TABLE_STATS
人工 SET_STATS统计信息被硬编码避免使用 DBMS_STATS.SET_STATS 除非做基准测试
事务未提交执行计划突然回退到全表确保查询在只读事务或 COMMIT 后执行

如何验证转换是否发生?

查看执行计划

EXPLAIN PLAN FOR
SELECT MAX(amt) FROM sales;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

出现 VIEW VW_SQT_% 即命中。

10053 跟踪

ALTER SESSION SET EVENTS '10053 trace name context forever, level 1';

在 trace 文件中搜索 SQTStatistics-based transformation


最佳实践:让统计信息成为“免费索引”

  1. 100% 采样 收集关键业务表的统计信息,尤其是大表;
  2. 定期自动任务 + 手动补充 结合,确保统计信息实时;
  3. 避免事务内跑报表
  4. 监控 STALE_STATS 标记,及时收集;
  5. 对只读报表库,考虑 LOCK_STATS 后彻底关闭自动收集。

当数据量到达亿级时,任何物理 I/O 都是昂贵的。Oracle 的统计信息查询转换把「元数据」变成了「索引」,在特定场景下提供了 零 I/O 的极致性能。理解并善用这一特性,能让你的大表查询从“秒”进化到“毫秒”,而这一切都发生在优化器内部,无需应用改动。

下次再看到 VW_SQT_%,别忘了对统计信息说声谢谢!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部