Oracle 数据库统计信息收集“避坑指南”


统计信息是优化器的大脑。信息失真,执行计划就“抽筋”。本文基于一次真实生产变更,将内部维护手册提炼成可落地的技术博客,包含:

  • 变更前如何备份
  • 如何优雅地收集统计信息(ANALYZE vs DBMS_STATS)
  • 变更后如何快速验证与回滚

1. 变更前:三件事必须做

步骤目的参考脚本
1.1 记录失效对象收集后对比,防止新失效create table t_invalid_objects as select * from dba_objects where status<>'VALID';
1.2 创建统计信息备份表把统计信息“倒”出来exec dbms_stats.create_stat_table(ownname=>'WWW',stattab=>'STAT_BAK20250816');
1.3 导出关键对象统计信息一键回滚plsql<br>exec dbms_stats.export_table_stats(<br> ownname => 'OWNER',<br> tabname => 'TABLE_NAME',<br> statown => 'WWW',<br> stattab => 'STAT_BAK20250816',<br> statid => 'ID20250816');<br>

小贴士:STAT_BAK20250816 这个表最好放在单独的统计信息表空间,防止和业务表空间耦合。


ANALYZE 已过时,DBMS_STATS 才是主流

为什么弃用 ANALYZE?

  • 不支持并行度、粒度、直方图等高级参数
  • 未来版本可能移除

整库轻量级收集(业务低峰)

BEGIN
dbms_stats.gather_schema_stats(
  ownname          => 'SCOTT',
  options          => 'GATHER AUTO',          -- 只动“空/过期”对象
  estimate_percent => dbms_stats.auto_sample_size,
  method_opt       => 'for all columns size repeat',
  degree           => 8,
  granularity      => 'AUTO',
  cascade          => TRUE,
  no_invalidate    => FALSE);                 -- 立即失效游标,立等生效
END;
/

单表深度收集(大表/核心表)

BEGIN
dbms_stats.gather_table_stats(
  ownname     => 'SCOTT',
  tabname     => 'ORDERS',
  partname    => 'P202508',                  -- 分区表可指定分区
  estimate_percent => 100,                   -- 大表可改回 AUTO
  method_opt  => 'for all columns size skewonly',
  degree      => 16,
  cascade     => TRUE,
  granularity => 'ALL',                      -- 分区+子分区+全局
  no_invalidate => FALSE);
END;
/

只刷索引(索引重建后)

BEGIN
dbms_stats.gather_index_stats(
  ownname => 'SCOTT',
  indname => 'IDX_ORDERS_CREATED',
  degree  => 8);
END;
/

变更后:3 分钟完成验证

检查项SQL
新增失效对象select object_name,owner from dba_objects where status<>'VALID' minus select object_name,owner from t_invalid_objects;
SQL 性能对比 AWR/ASH 中 Top SQL 的 elapsed time
执行计划select * from table(dbms_xplan.display_cursor(sql_id=>'xxxx'));

如果 no_invalidate=>FALSE 会带来瞬间硬解析风暴,可配合 alter system flush shared_pool; 后观察 v$librarycache 的 reloads。

Q1:收集后执行计划反而变差? A:99% 是直方图“翻车”。用 method_opt=>'for all columns size 1' 删除直方图再试。

Q2:可以一边跑业务一边收集吗? A:可以,把 no_invalidate 设默认 DBMS_STATS.AUTO_INVALIDATE,业务无感知,但 SQL 可能要等下一次执行才用上新的统计信息。

Q3:统计表占空间太大怎么办? A:定期 exec dbms_stats.purge_stats(sysdate-7); 清理历史。

备份是底线,粒度是关键,回滚是底气

发表评论

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

滚动至顶部