应优先通过DBA_SEGMENTS计算表空间真实使用率以规避DBA_FREE_SPACE碎片失真,再定位TOP20大段并区分LOB/INDEX等类型,最后关联查询LOB和索引归属表,同时排查临时段与UNDO段异常增长。
查表空间整体使用率:先看“谁最挤”,再深入挖
表空间爆满时,第一反应不是直接翻对象,而是确认哪个表空间真的告急。dba_free_space 在高碎片场景下统计失真,尤其当使用率超过 90% 时,它可能把大量无法合并的小空闲块算作“可用”,误导判断。这时候必须以 dba_segments 为基准——它反映的是已分配段的真实字节数,不依赖空闲块合并状态。
- 运行这条 SQL 获取真实使用率(按段计算):
SELECT df.tablespace_name AS "表空间名",<br> ROUND(SUM(df.bytes)/1024/1024,2) AS "总大小(MB)",<br> ROUND(SUM(s.bytes)/1024/1024,2) AS "已使用(MB)",<br> ROUND((SUM(df.bytes)-SUM(s.bytes))/1024/1024,2) AS "剩余空间(MB)",<br> ROUND(SUM(s.bytes)/SUM(df.bytes)*100,2) AS "使用率(%)"<br>FROM dba_data_files df<br>JOIN dba_segments s ON df.tablespace_name = s.tablespace_name<br>GROUP BY df.tablespace_name<br>ORDER BY "使用率(%)" DESC;
- 注意
SUM(s.bytes)是关键:它绕过了DBA_FREE_SPACE的碎片盲区,直接从“已分配”角度算账 - 如果某表空间显示使用率 98%,但
DBA_FREE_SPACE报出还有 500MB 空闲,大概率是碎片严重,不能信
定位前 20 大段:聚焦 DBA_SEGMENTS,别只盯表
很多人查完表空间发现 USERS 占了 95%,就立刻去 DBA_TABLES 找大表——这会漏掉真正吃空间的“隐形大户”。一张带 CLOB 字段的表,其 LOBSEGMENT 和 LOBINDEX 可能比表段本身还大好几倍;一个高频更新的索引,INDEX 段也可能远超基表。
- 用这个查询直击表空间内真实空间占用 TOP 20:
SELECT owner, segment_name, segment_type, partition_name,<br> ROUND(bytes/1024/1024,2) AS "大小(MB)"<br>FROM dba_segments<br>WHERE tablespace_name = 'USERS'<br>ORDER BY bytes DESC<br>FETCH FIRST 20 ROWS ONLY;
-
segment_type必须关注:看到LOBSEGMENT、INDEX、TABLE PARTITION都要单独记下来,不能默认只有TABLE才重要 - 如果结果里出现大量
UNDO或TEMPORARY类型段,说明有长事务或排序操作未结束,得结合V$TRANSACTION或V$TEMPSEG_USAGE追踪
关联 LOB 和索引:搞清“谁在背后占空间”
查到一个大 LOBSEGMENT 名叫 SYS_LOB0000092320C00005$$,你肯定不认识——它不会告诉你属于哪张表。同理,一个名字像 IDX_ORD_CUSTID 的索引,你也得确认它是不是真挂载在核心订单表上,还是某个测试表的残留。
- 查 LOB 所属表:
SELECT l.owner, l.table_name, l.column_name, s.bytes/1024/1024 AS "lob_size_mb"<br>FROM dba_lobs l<br>JOIN dba_segments s ON l.segment_name = s.segment_name<br>WHERE s.tablespace_name = 'USERS' AND s.segment_type = 'LOBSEGMENT';
- 查索引归属的基表:
SELECT i.owner, i.index_name, i.table_owner, i.table_name, s.bytes/1024/1024 AS "idx_size_mb"<br>FROM dba_indexes i<br>JOIN dba_segments s ON i.index_name = s.segment_name AND i.owner = s.owner<br>WHERE s.tablespace_name = 'USERS' AND s.segment_type = 'INDEX';
- 特别注意:
dba_lobs中的segment_name和dba_segments中的完全一致,这是唯一可靠关联点;别用table_name去 join,因为分区表的 LOB 段名不含表名
临时段和 UNDO 段异常增长:容易被忽略的“空间刺客”
有时候你查遍了 TABLE、INDEX、LOBSEGMENT,TOP 20 里没一个明显异常,但表空间还在涨——八成是临时段或 UNDO 段在偷偷吃空间。它们不持久,但一次大排序或未提交事务就能瞬间拉高使用率。
- 查当前活跃临时段:
SELECT s.username, u.segtype, u.contents, u.extents, u.blocks*8/1024 AS "mb_used"<br>FROM v$sort_usage u<br>JOIN v$session s ON u.session_addr = s.saddr;
- 查 UNDO 表空间中长事务:
SELECT s.sid, s.serial#, s.username, t.used_ublk, t.start_time<br>FROM v$transaction t<br>JOIN v$session s ON t.ses_addr = s.saddr<br>WHERE t.start_time < SYSDATE - 1/24; -- 超过 1 小时的事务
- 临时段不会出现在
DBA_SEGMENTS里(除非你显式创建了永久临时段),所以必须用v$sort_usage;UNDO 段则只在UNDO表空间内,若你在 USERS 里看到UNDO类型段,说明配置错误,得马上纠正
查清楚不是终点。真正难的是区分:哪些是业务必需的大对象,哪些是可归档的历史分区,哪些是开发留下的测试索引——这些判断没法靠 SQL 给出答案,得翻日志、问业务、看上线记录。空间分析的门槛不在语法,而在知道该怀疑谁、该问什么人。










