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