场景:操作系统相关文件系统占用马上100%,查看发现文件都是数据库的数据文件,想着把对应的表空间进行收缩下,不然100%会出现问题。

1.识别占用大量文件系统空间的表空间
-- 需关注 size_mb 大但是 pct_used 低的表空间。这些表空间实际使用率不高,是收缩的目标。
--free_mb 显示的是数据文件内当前标记为“空闲”的空间,但这部分空间通常 不是 能通过 RESIZE 释放给操作系统的空间。真正能释放的是 HWM 之上的空间(需要通过重组表来回收)。
SELECT
tablespace_name,
file_name,
ROUND(bytes / 1024 / 1024, 2) AS size_mb,
ROUND((bytes - NVL(free_space, 0)) / 1024 / 1024, 2) AS used_mb,
ROUND(NVL(free_space, 0) / 1024 / 1024, 2) AS free_mb,
ROUND((bytes - NVL(free_space, 0)) / bytes * 100, 2) AS pct_used
FROM
(SELECT file_id, file_name, tablespace_name, bytes FROM dba_data_files)
JOIN
(SELECT file_id, SUM(bytes) AS free_space FROM dba_free_space GROUP BY file_id)
USING (file_id)
ORDER BY size_mb DESC;
查看user_simis表空间下所有的段大小合计为601g,和userd_mb几乎一样。
select sum(bytes/1024/1024/1024) TSize_GB from dba_segments where tablespace_name='USER_SIMIS' ;

2.解读dba_free_space
free_mb:已经分配给oracle数据文件,尚未被数据库对象(表、索引)占用的空间。这些空间可以直接用来存储新数据或扩展现有对象,不需要向操作系统申请新的文件系统空间。现在还有89751M没有使用
文件系统空间:如创建100g数据文件,操作系统会立即在文件系统上分配这100g
数据文件内部:初始可能是只有一小部分数据库的段占用,比如10g。剩余90g是空闲的记录在free_mb
3.是否可以收缩表空间
resize只能释放数据文件物理末尾、连续的、未被使用的空间,无法释放文件中间或开头的空闲空间。
比如大部分空闲遍布在不同位置,高水位线后连续未被使用的只有很小的1g
--查找指定数据文件末尾的最大连续空闲空间 (单位 MB):
SELECT
ddf.file_id,
file_name,
ROUND((blocks * block_size) / (1024 * 1024), 2) AS current_size_mb,
ROUND((NVL(hwm_blocks, 1) * block_size) / (1024 * 1024), 2) AS hwm_position_mb,
ROUND((blocks - NVL(hwm_blocks, 1)) * block_size / (1024 * 1024), 2) AS resizable_space_mb -- 这就是能RESIZE释放的最大空间
FROM
dba_data_files ddf,
(SELECT
file_id, MAX(block_id + blocks) AS hwm_blocks
FROM
dba_extents
GROUP BY
file_id) de,
(SELECT value AS block_size FROM v$parameter WHERE name = 'db_block_size') p
WHERE
ddf.file_id = de.file_id(+)
ORDER BY
file_id;可以发现USERSIMIS最多能缩小1.3M(文件末尾连续空闲空间),太小了,无法resize
resize可释放的空间=hwm之后的空间,即使hwm之前大量空闲块如free_mb不能释放
注意:resize不会移动hwm,释放hwm需要重组对象

4.查看碎片化较高的表
此处我们选择表:SIMIS.AC60有数据行64445542
注意:move需要2倍的空间,如果空间不够会报错。
--查找高碎片化表(按碎片空间排序)
SELECT owner, table_name,
ROUND((blocks * block_size) / 1024 / 1024, 2) AS allocated_mb,
ROUND((num_rows * avg_row_len) / 1024 / 1024, 2) AS actual_data_mb,
ROUND((blocks * block_size - num_rows * avg_row_len) / 1024 / 1024, 2) AS fragment_mb,
(blocks * block_size - num_rows * avg_row_len) / (blocks * block_size) * 100 AS fragment_pct
FROM dba_tables
JOIN dba_tablespaces ON (dba_tables.tablespace_name = dba_tablespaces.tablespace_name)
WHERE dba_tables.tablespace_name = 'USER_SIMIS'
AND blocks > 0
ORDER BY fragment_mb DESC;
5.查看表区块信息
显示用了990304个blocks,0个空闲blocks,这时候的990304 BLOCKS即是高水位线。
select SEGMENT_NAME,EXTENTS,BLOCKS,INITIAL_EXTENT,INITIAL_EXTENT/1024/1024 init from DBA_segments where owner='SIMIS' and SEGMENT_NAME='AC60';

select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from dba_tables a where a.TABLE_NAME='AC60' and a.OWNER='SIMIS';

6.进行move操作
alter table SIMIS.AC60 move tablespace USER_SIMIS;
查询move进度
SELECT sid, serial#, opname, sofar, totalwork, units ,start_time,message
FROM v$session_longops
WHERE opname LIKE '%Scan%' order by start_time desc;


7.重建失效索引
select owner,index_name,status from dba_indexes a where a.table_name='AC60' AND A.owner='SIMIS';

alter index SIMIS.IDX_AAC221_AAC224 rebuild online;


8.收集表统计信息(需要重建失效索引后才能进行收集否则收集会报错)
analyze table SIMIS.AC60 compute statistics; #用时33分钟
9.再次查询高水位线
select SEGMENT_NAME,EXTENTS,BLOCKS,INITIAL_EXTENT,INITIAL_EXTENT/1024/1024 init from DBA_segments where owner='SIMIS' and SEGMENT_NAME='AC60';

select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from dba_tables a where a.TABLE_NAME='AC60' and a.OWNER='SIMIS';

10.查询释放数据量
1283.77-1125.65=158.12M释放了158M,释放的空间可能不连续,后续resize的时候可以先尝试收缩看看,但是不能保证成功,因为释放的空间很少,且有可能都在HWM以下。

11.收缩表空间
由于我收缩的表释放的空间不多,且都是hwm线以下的空间,收缩会提示低于最小容许大小的错误。
如果HWM或任何数据块在指定大小701479之后,resize会失败,数据文件可能还有数据或hwm高于这个701479M点,所以不能收缩。resize必须大于或等于HWM。
大家后续尝试的时候可以找碎片化程度高的表,多收缩几个表,然后resize应该是没问题的!!
ALTER DATABASE DATAFILE '/wbdata5fs/oradata/USER_SIMIS_01' RESIZE 701479M;





