dba_hist_seg_stat 是定位段级增长最有效的办法,因其记录每小时快照的 db_block_changes_delta 增量,可精准反映某时段内各段写入的数据块变化;而 dba_segments.bytes 仅显示当前已分配空间,不体现历史增量,无法用于趋势分析。
直接查 dba_hist_seg_stat 是定位段级增长最有效的办法,但必须配合时间窗口过滤和块变化量阈值,否则结果毫无意义。
为什么不能只看 DBA_SEGMENTS.BYTES?
DBA_SEGMENTS.BYTES 只反映当前已分配空间,不体现历史增量;高水位线不回落时,它长期不变,完全无法用于趋势分析。真正要抓的是“某段时间内谁写了最多数据块”。
- 段增长本质是
DB_BLOCK_CHANGES_DELTA的累积,这个字段在DBA_HIST_SEG_STAT中每小时快照一次 - 注意:该视图中
DB_BLOCK_CHANGES_DELTA是差值(上一快照到本次快照的增量),不是累计值 - 若没设 AWR 保留期或未启用统计收集,
DBA_HIST_SEG_STAT可能为空 —— 先确认SELECT retention FROM dba_hist_wr_control;
怎么查出增长最快的表?
核心是关联 DBA_HIST_SEG_STAT、DBA_OBJECTS 和 DBA_HIST_SNAPSHOT,按时间聚合并排序。下面这条语句能快速定位过去7天内增长最多的用户表:
SELECT a.owner, a.object_name, a.object_type, TRUNC(c.end_interval_time) snap_day, SUM(b.db_block_changes_delta) / 1024 / 1024 AS gb_used FROM dba_objects a JOIN dba_hist_seg_stat b ON a.object_id = b.obj# JOIN dba_hist_snapshot c ON b.snap_id = c.snap_id WHERE b.db_block_changes_delta > 5000 AND a.object_type = 'TABLE' AND c.end_interval_time >= SYSDATE - 7 GROUP BY a.owner, a.object_name, a.object_type, TRUNC(c.end_interval_time) ORDER BY gb_used DESC FETCH FIRST 10 ROWS ONLY;
-
DB_BLOCK_CHANGES_DELTA > 5000是合理起点,太小会淹没在系统对象噪声里 - 必须加
TRUNC(c.end_interval_time)聚合到天粒度,否则单次大事务会扭曲日趋势 - 别漏掉
FETCH FIRST 10 ROWS ONLY,否则可能返回数万行
DBA_HIST_TBSPC_SPACE_USAGE 为什么算不准段级来源?
它只告诉你“表空间用了多少块”,但不知道这些块是谁占的。TABLESPACE_USEDSIZE 是以数据块为单位的总量,和 DBA_HIST_SEG_STAT 的粒度完全不同,两者无法直接映射。
- 同一个表空间可能有多个块大小(比如 8K 和 16K),
TABLESPACE_USEDSIZE * block_size才是真实字节数 - 它不区分段类型:索引、LOB、临时段、undo 段全混在一起,没法下钻到具体表
- 如果想用它反推增长,只能辅助验证:
LAG(used_gb) OVER (ORDER BY rtime)看是否和DBA_HIST_SEG_STAT中头部几张表的增长量吻合
容易被忽略的权限与采样陷阱
即使有 SELECT_CATALOG_ROLE,也不代表能访问全部历史数据 —— 多租户环境下,CDB_HIST_SEG_STAT 和 DBA_HIST_SEG_STAT 行为不同;而 AWR 快照默认每小时一次,但大事务可能跨多个快照,导致增长被拆散。
- 查 CDB 环境必须显式指定
CON_ID,否则只看到 CDB$ROOT 的数据 -
DBA_HIST_SEG_STAT中的DATAOBJ#比OBJ#更可靠,因为对象重定义后OBJ#会变,DATAOBJ#不变 - 如果发现某张表在快照间突增 10GB,但开发说没做任何操作,大概率是隐式 LOB 写入、物化视图刷新或分区交换引起的











