想象一个 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 |
NDV | HIST_HEAD$ | 2,147,483 |
只要这些值满足查询语义,优化器就能直接返回结果。
触发条件:不是所有 MAX() 都能走捷径
维度 | 允许转换 | 不允许转换 |
---|---|---|
聚合函数 | MIN , MAX , COUNT(*) , APPROX_COUNT_DISTINCT | SUM , 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 => 100 或 AUTO_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 文件中搜索 SQT
或 Statistics-based transformation
。
最佳实践:让统计信息成为“免费索引”
- 100% 采样 收集关键业务表的统计信息,尤其是大表;
- 定期自动任务 + 手动补充 结合,确保统计信息实时;
- 避免事务内跑报表;
- 监控 STALE_STATS 标记,及时收集;
- 对只读报表库,考虑
LOCK_STATS
后彻底关闭自动收集。
当数据量到达亿级时,任何物理 I/O 都是昂贵的。Oracle 的统计信息查询转换把「元数据」变成了「索引」,在特定场景下提供了 零 I/O 的极致性能。理解并善用这一特性,能让你的大表查询从“秒”进化到“毫秒”,而这一切都发生在优化器内部,无需应用改动。
下次再看到 VW_SQT_%
,别忘了对统计信息说声谢谢!