user_segments的bytes是段预分配空间而非实际使用量;实际数据量需结合num_rows×avg_row_len(须先gather stats);精确空间使用应调用dbms_space.object_space_usage获取used_bytes;lob和索引段需单独统计。
直接看 user_segments 返回的 bytes 值,只是该表段预分配的总空间,不是实际用了多少。真要算“实际用了多少”,必须结合统计信息 + 空间使用分析,否则容易把空块、hwm以下未回收块、行迁移开销全算成“已用”。
查预分配空间:user_segments 或 dba_segments 是唯一可靠来源
这是 Oracle 物理分配的底线——只要段存在,这部分空间就从表空间里划走了,哪怕里面一条记录都没有。
-
user_segments适用于当前用户下的表,权限要求低;dba_segments要 DBA 权限,能查所有 owner - 注意
segment_type = 'TABLE'必须显式指定,否则可能混入 IOT overflow、nested table 等附属段 - 如果表有 LOB 列,
user_segments会返回多个记录(主表 + LOBSEGMENT + LOBINDEX),需按segment_name和segment_type分组求和 -
bytes是准确值,但blocks可能因 ASSM 或手动段管理略有偏差,优先信bytes
示例:SELECT segment_name, ROUND(SUM(bytes)/1024/1024, 2) AS mb FROM user_segments WHERE segment_type = 'TABLE' AND segment_name = 'ORDERS' GROUP BY segment_name;
算实际数据占用:依赖准确的 num_rows * avg_row_len,但前提得先 gather stats
这个乘积给出的是纯数据行的字节估算,不含块头、PCTFREE、行迁移、空闲空间等开销,是逻辑层最接近“真实体积”的指标。
- 必须执行
DBMS_STATS.GATHER_TABLE_STATS,ANALYZE TABLE ... COMPUTE STATISTICS已过时且不兼容新版本 CBO -
avg_row_len在user_tables中,但若表刚建或长期没 gather,该值为NULL或严重失真 - 对含 LOB、LONG、对象类型列的表,
avg_row_len不包含 LOB 数据本身(只算指针),LOB 实际体积需单独查user_lobs+user_segments - 结果单位是字节,除以
1024*1024得 MB,但别四舍五入太狠——小表可能只剩 KB 级,四舍五入到小数点后两位会归零
示例:SELECT table_name, ROUND(num_rows * avg_row_len / 1024 / 1024, 3) AS data_mb FROM user_tables WHERE table_name = 'ORDERS';
看空间浪费程度:用 dbms_space.object_space_usage 查 HWM 和未格式化块
这是唯一能告诉你“分配了但完全没碰过”的块有多少的方法,尤其适合判断是否值得 shrink 或 move。
- 必须用 PL/SQL 执行,不能写成纯 SQL 查询;输出参数
used_bytes是真正被数据占据的空间(含行头、事务槽等),比num_rows * avg_row_len更精确 -
alloc_bytes应该和user_segments.bytes基本一致,若差异大,说明有段级异常(比如大量空 extent 未释放) -
unformatted_blocks> 0 表示 HWM 以下有从未写入过的块,shrink 可回收;chain_percent> 5% 就该检查是否需要调大pctfree或重建表 - 注意:该过程对分区表要指定
partition_name,否则只返回主分区结果
示例块:
DECLARE
l_used NUMBER;
l_alloc NUMBER;
l_unfmt NUMBER;
BEGIN
DBMS_SPACE.OBJECT_SPACE_USAGE(
segment_owner => 'SCHEMA_NAME',
segment_name => 'ORDERS',
segment_type => 'TABLE',
partition_name=> NULL,
used_bytes => l_used,
alloc_bytes => l_alloc,
unformatted_blocks => l_unfmt
);
DBMS_OUTPUT.PUT_LINE('Used: ' || ROUND(l_used/1024/1024, 2) || ' MB');
DBMS_OUTPUT.PUT_LINE('Alloc: ' || ROUND(l_alloc/1024/1024, 2) || ' MB');
DBMS_OUTPUT.PUT_LINE('Unformatted blocks: ' || l_unfmt);
END;
别忽略 LOB 和索引:它们不在主表段里,但算总成本时必须加进去
一张表的空间账,光看主段是瘸腿的。LOB 列和普通索引各自独立成段,user_segments 里查不到它们和主表的归属关系,只能靠名字推断或连查 user_lobs/user_indexes。
-
user_lobs的segment_name字段就是 LOB 数据段名,index_name是 LOB 索引段名,两者都要去user_segments里单独 sum -
user_indexes的index_name直接对应user_segments.segment_name,类型为INDEX - 外键索引、函数索引、位图索引都算,别漏掉;IOT 的主键索引就是表本身,不用重复加
- 如果表被频繁 update,索引段可能比主表段还大——尤其是高基数、宽字段的复合索引
最容易被跳过的其实是 LOBINDEX 段:它虽小,但每个 LOB 行都依赖它定位,且无法 shrink,长期积累也会占几 MB。











