show_space不能替代全景分析,因其仅针对单个段返回已用/未用块信息,无法自动关联该表的lob段、索引段等附属对象,导致空间统计严重低估。
直接查 dba_segments 只能看到表段,但真实空间消耗往往藏在 lob 段、索引段甚至分区子段里;用存储过程(比如 show_space)能拿到更细粒度的已用/未用块信息,但必须注意它不统计附属对象——表本身占多少、lob 占多少、索引占多少,得分开查、再加总。
为什么 show_space 不能替代全景分析?
show_space 是 Tom Kyte 编写的经典 PL/SQL 存储过程,它调用 DBMS_SPACE.UNUSED_SPACE,返回单个段(如一张表)的分配块数、已用块数、空闲块数等。但它只作用于一个 segment_name + segment_type 组合,无法自动关联该表的 LOB 段或索引段。
常见错误现象:
- 执行
exec show_space('ORDER_HEADER')显示 “Total bytes: 1.2GB”,但实际业务反馈磁盘涨了 8GB —— 很可能这张表有 3 个 CLOB 字段,对应 3 个独立 LOBSEGMENT,每个都占 2GB+ - 查询结果中
unused blocks为 0,误以为空间已满,其实只是高水位线(HWM)没下降,ALTER TABLE ... SHRINK SPACE才能释放
使用场景:适合对单个疑似膨胀对象做深度诊断,比如怀疑某张表存在大量行迁移或碎片;不适合批量盘点或容量规划。
怎么用 DBA_SEGMENTS 查全一张表的真实开销?
一张业务表的空间 = 表段(TABLE / TABLE PARTITION) + 所有 LOB 段(LOBSEGMENT) + 所有索引段(INDEX / INDEX PARTITION)。必须三路数据关联聚合,不能只查 segment_name = 'XXX'。
关键参数差异:
-
SEGMENT_TYPE IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION'):覆盖普通表和所有分区形态 -
SEGMENT_TYPE = 'LOBSEGMENT':必须通过DBA_LOBS关联到基表,不能靠名字猜测(如'SYS_LOB0000123456$'无法反推原表) -
SEGMENT_TYPE IN ('INDEX', 'INDEX PARTITION'):需用DBA_INDEXES.TABLE_NAME关联回原表,注意大小写和 owner
示例片段(简化版):
SELECT
t.owner, t.table_name,
ROUND(SUM(CASE WHEN s.segment_type LIKE 'TABLE%' THEN s.bytes ELSE 0 END)/1024/1024/1024, 2) AS table_gb,
ROUND(SUM(CASE WHEN s.segment_type = 'LOBSEGMENT' THEN s.bytes ELSE 0 END)/1024/1024/1024, 2) AS lob_gb,
ROUND(SUM(CASE WHEN s.segment_type LIKE 'INDEX%' THEN s.bytes ELSE 0 END)/1024/1024/1024, 2) AS idx_gb
FROM dba_tables t
JOIN dba_segments s ON (
(s.segment_type LIKE 'TABLE%' AND s.segment_name = t.table_name AND s.owner = t.owner) OR
(s.segment_type = 'LOBSEGMENT' AND s.segment_name IN (
SELECT l.segment_name FROM dba_lobs l WHERE l.table_name = t.table_name AND l.owner = t.owner
)) OR
(s.segment_type LIKE 'INDEX%' AND s.segment_name IN (
SELECT i.index_name FROM dba_indexes i WHERE i.table_name = t.table_name AND i.table_owner = t.owner
))
)
GROUP BY t.owner, t.table_name
ORDER BY (table_gb + lob_gb + idx_gb) DESC;
什么时候该信 DBMS_SPACE.OBJECT_SPACE_USAGE?
这个函数比 UNUSED_SPACE 更进一步,能区分“已格式化但未使用”和“完全未格式化”的空间,适用于 ASSM(Automatic Segment Space Management)表空间,且支持分区对象。
但要注意:
- 需要先执行
EXEC DBMS_STATS.GATHER_TABLE_STATS,否则返回的used_bytes可能为 0 或严重偏低 - 对大对象(LOB)默认不计入,必须显式传入
segment_type => 'LOB'并指定segment_name(即 LOBSEGMENT 名) - 返回的是字节级精确值,但不包含索引——仍要单独查
dba_segments
性能影响:对超大表(>100GB)执行一次 OBJECT_SPACE_USAGE 可能耗时数秒,不适合在监控脚本中高频轮询。
容易被忽略的三个硬伤
第一,DBA_SEGMENTS.BYTES 是分配空间,不是文件系统级占用。如果启用了数据压缩(COMPRESS FOR OLTP),物理磁盘占用会小得多,但 BYTES 不体现压缩率。
第二,临时段(TEMPORARY 类型)、回滚段(UNDO)、物化视图日志(MATERIALIZED VIEW LOG)不会出现在常规表关联中,但它们也吃表空间——排查空间突增时必须单独查 DBA_SEGMENTS.SEGMENT_TYPE IN ('TEMPORARY','UNDO','MATERIALIZED VIEW LOG')。
第三,回收站(RECYCLEBIN)里的表仍算作有效段,DBA_SEGMENTS 默认包含它们。清空前务必确认:SELECT * FROM RECYCLEBIN WHERE OWNER = 'xxx',否则空间分析永远差一大截。











