查表空间真实使用率必须用DBA_SEGMENTS,因其统计所有段实际占用空间,不受空闲块碎片影响;DBA_FREE_SPACE在高碎片时严重失真,尤其使用率>90%不可信。
查表空间真实使用率必须用 DBA_SEGMENTS,别信 DBA_FREE_SPACE
当 dba_free_space 显示表空间还有 10% 空间,但实际新增数据就报 ora-01653,大概率是空闲块太碎、无法分配连续 extent。这时候 dba_segments 才反映真实已分配字节数——它统计的是所有段(表、索引、lob、临时段等)实际占的空间,不依赖空闲块是否能合并。
直接执行这个语句看每个表空间的真实占用:
SELECT tablespace_name,
ROUND(SUM(bytes)/1024/1024/1024, 2) AS "GB_USED"
FROM dba_segments
GROUP BY tablespace_name
ORDER BY "GB_USED" DESC;
-
DBA_FREE_SPACE在高碎片场景下严重失真,尤其当使用率 >90% 时,基本不可信 -
DBA_SEGMENTS不包含临时段和 undo 段(它们在V$TEMPSEG_USAGE和V$UNDOSTAT中),需单独查 - 如果某表空间在
DBA_SEGMENTS中没记录,说明它目前没被任何持久段使用(可能是只放了 temp/undo,或刚建好还没写入)
定位具体用户:按 owner 分组查 DBA_SEGMENTS
知道哪个表空间挤后,下一步是揪出“大户”用户。注意:不能只查 DBA_USERS 的 DEFAULT_TABLESPACE,因为用户对象可以建在任意表空间里,和默认值无关。
运行这个语句,按用户汇总其在所有表空间的总占用:
SELECT owner,
ROUND(SUM(bytes)/1024/1024/1024, 2) AS "GB_TOTAL"
FROM dba_segments
GROUP BY owner
ORDER BY "GB_TOTAL" DESC;
- 结果里常见的系统用户如
SYS、SYSTEM、OUTLN通常不大,若它们异常高,先检查是否误存了大日志表或归档对象 - 业务用户名一般全大写,比如
TMS21、FINANCE_APP,优先盯这些 - 如果看到
OWNER是空值(NULL),说明是全局临时表(GLOBAL TEMPORARY TABLE)的 segment,归属会话而非用户,得结合V$TEMPSEG_USAGE查当前活跃会话
查清一个用户在哪个表空间占了多少:带 segment_type 细分
光知道用户总用量不够,得知道钱花在哪了——是表?索引?还是 LOB?不同对象类型清理策略完全不同。
用这个语句拆解指定用户(比如 'TMS21')在各表空间的分布:
SELECT tablespace_name,
segment_type,
COUNT(*) AS "OBJECT_COUNT",
ROUND(SUM(bytes)/1024/1024, 2) AS "MB_USED"
FROM dba_segments
WHERE owner = 'TMS21'
GROUP BY tablespace_name, segment_type
ORDER BY "MB_USED" DESC;
-
SEGMENT_TYPE = 'LOBSEGMENT'最容易被忽略:名字像SYS_LOB0000092320C00005$$,完全看不出归属哪张表,必须关联DBA_LOBS查源头 -
SEGMENT_TYPE = 'INDEX'占比高,说明该用户建了大量索引,或者有未使用的冗余索引 -
SEGMENT_TYPE = 'TABLE'是主因,但要再往下查具体哪张表——加segment_name分组就能看到
LOB 和索引找不到归属表?必须连查 DBA_LOBS 和 DBA_INDEXES
看到 SYS_LOB0000092320C00005$$ 这种名字别慌,它只是物理段名,逻辑上一定属于某张表的某个 CLOB/BLOB 字段。同样,索引名也不一定和表名对应。
查 LOB 归属表:
SELECT l.owner, l.table_name, l.column_name, s.bytes
FROM dba_lobs l
JOIN dba_segments s ON s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type = 'LOBSEGMENT'
WHERE s.segment_name = 'SYS_LOB0000092320C00005$$';
查索引归属表:
SELECT i.owner, i.table_name, i.index_name, s.bytes
FROM dba_indexes i
JOIN dba_segments s ON s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type = 'INDEX'
WHERE s.segment_name = 'IDX_USER_EMAIL';
- LOB 查询必须用
dba_lobs关联,不能靠名字猜;否则可能删错段,导致表字段变 NULL - 索引查询中,
i.table_name是真实归属表,哪怕索引名里带 “USER” 也可能建在ORDER_LOG上 - 这两个关联查询都要求
s.owner和i.owner/l.owner一致,跨用户建索引或 LOB 虽少见,但存在
真正难的不是写出 SQL,而是意识到 DBA_SEGMENTS 里那些匿名 LOB 段、临时段、分区子段,它们不会出现在 DBA_TABLES 或 DBA_INDEXES 的常规列表里——漏掉任何一个,空间分析就缺一块拼图。


















