暂无图片
暂无图片
2
暂无图片
暂无图片
暂无图片

So Easy! Oracle 23AI 的表空间收缩就是这么简单

原创 听见风的声音 2025-11-18
772

1 一个常见的场景

在数据库运维中,表或索引的删除是常见的操作,删除后,表空间里会有很多剩余空间。有时,需要将这些回收到存储或者操作系统里,用作其它用途。
在Oracle 23AI之前,这个操作也可以作,但是操作起来有点难度,尤其是一个表空间包括很多数据文件时,要回收表空间的空间需要对单独的数据文件进行resize操作。Oracle 23AI提供了表空间的收缩工作,最初,这个特性只支持bigfile 表空间,后来,Oracle又将这个特定扩展到了smallfile表空间上。

2 bigfile和smallfile表空间

bigfile表空间内只能由一个数据文件,这个数据文件很大,最大可以支持到4GB个数据块。传统的标准的表空间又被称为smallfile表空间,一个表空间内最多可以支持1023(oracle 12c及以后版本)个数据文件,每个数据文件最大可以支持到2的22次方个数据块,在数据块大小为8KB时,每个数据文件支持32GB存储空间。
从Oracle 23ai开始,SYSAUX, SYSTEM, and USER 表空间缺省都是bigfile表空间。

2 表空间收缩存储过程语法及使用

Oracle 23AI中,收缩表空间要使用DBMS_SPACE包中的SHRINK_TABLESPACE存储过程,这个存储过程的语法如下

PROCEDURE SHRINK_TABLESPACE Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- TS_NAME VARCHAR2 IN SHRINK_MODE NUMBER IN DEFAULT TARGET_SIZE NUMBER IN DEFAULT SHRINK_RESULT CLOB OUT

存储过程的语法比较简单,只有4个参数,TS_NAME指定要收缩的表空间的名称,TARGET_SIZE设置收缩的目标,即将表空间收缩到多大,SHRINK_RESULT设置收缩操作形成的操作报告存储在哪里。SHRINK_MODE设置操作模式,表空间收缩共有3种模式,如下:

  • TS_MODE_ANALYZE 只做分析,不执行。
  • TS_MODE_SHRINK 缺省,只执行在线移动数据对象操作
  • TS_MODE_SHRINK_FORCE 默认执行在线移动数据对象操作, 但是如果在线移动失败,将尝试执行对象的离线移动操作。

这个存储过程的四个参数中,有默认值的不是强制参数,可以不提供,使用默认值,没有默认值的则必须提供。

3 表空间收缩分析

在执行表空间收缩之前,应先执行一下收缩分析,看一下收缩会达到的效果,如下

SQL> set long 10000 SQL> variable result clob --定义变量用于输入分析操作形成的分析报告,这个时强制的变量,如果不定义这个变量,存储过程执行会报错 SQL> col result format a200 SQL> execute DBMS_SPACE.SHRINK_TABLESPACE('RECLAIMTS', SHRINK_MODE => DBMS_SPACE.TS_MODE_ANALYZE, SHRINK_RESULT => :result); PL/SQL procedure successfully completed. SQL> print result RESULT ---------------------------------------------------------------------------------- Movable Objects: 1. { RECLAIM_USER.T2 | type: TABLE | blocks: 20480 | tablespace_name: RECLAIMTS } 2. { RECLAIM_USER.T2_PK | type: INDEX | blocks: 32 | tablespace_name: RECLAIMTS } Total Movable Objects: 2 Total Movable Size(GB): .16 Original Datafile Size(GB): 1.01 Suggested Target Size(GB): .38 Process Time: +00 00:00:01.669704

报告显示了可以移动的对象,移动的大小,数据文件初始的大小,建议移动的目标大小。

4 表空间收缩实际操作

1)操作前的准备工作

a 创建表空间

QL> create bigfile tablespace reclaimts datafile size 10M autoextend on next 5M; --创建表空间 Tablespace created. SQL> create user reclaim_user identified by reclaim default tablespace reclaimts quota unlimited on reclaimts;--创建用户使用前面创建的表空间 User created. SQL> grant connect, resource, dba to reclaim_user; --给用户授权 Grant succeeded.

b 创建表,载入数据

SQL> connect reclaim_user/reclaim@<servicename> SQL> create table t1 ( id number, col1 varchar2(4000), col2 varchar2(4000), constraint t1_pk primary key (id)); SQL> create table t2 ( id number, col1 varchar2(4000), col2 varchar2(4000), constraint t2_pk primary key (id)); SQL> insert /*+append*/ into t1 select rownum, rpad('x', 4000, 'x'), rpad('x', 4000, 'x') from dual connect by level <= 10000; 10000 rows created. SQL> commit; SQL> insert /*+append*/ into t2 select rownum, rpad('x', 4000, 'x'), rpad('x', 4000, 'x') from dual connect by level <= 10000; 10000 rows created. SQL> commit;

c 收集兑现统计信息,检查对象大小

SQL> exec dbms_stats.gather_schema_stats('RECLAIM_USER');--收集RECLAIM_USER用户的对象统计信息 PL/SQL procedure successfully completed. SQL> col tablespace_name format a25 SQl> select tablespace_name, blocks, bytes/1024/1024 as size_mb from dba_data_files where tablespace_name = 'RECLAIMTS'; TABLESPACE_NAME BLOCKS SIZE_MB ------------------------- ---------- ---------- RECLAIMTS 132480 1035 SQL> col segment_name format a10 SQL> select segment_name, segment_type, bytes/1024 size_kb from user_segments; SEGMENT_NA SEGMENT_TY SIZE_KB ---------- ---------- ---------- T1 TABLE 163840 T2 TABLE 163840 T1_PK INDEX 256 T2_PK INDEX 256
2)模拟删除操作,截断其中一个表

a 截断表后收集统计信息

SQL> truncate table t1; Table truncated. SQL> exec dbms_stats.gather_schema_stats('RECLAIM_USER'); PL/SQL procedure successfully completed.

b 检查表空间信息

SQL> col segment_name format a10 SQL> select segment_name, segment_type, bytes/1024 size_kb from user_segments; --表空间里的分段信息 SEGMENT_NA SEGMENT_TY SIZE_KB ---------- ---------- ---------- T1 TABLE 64 T1_PK INDEX 64 T2 TABLE 163840 T2_PK INDEX 256 SQL> select ts.tablespace_name, sum(nvl(fs.bytes,0))/1024/1024 as MB_FREE, --表空间信息      count(*) as FRAGMENTS, max(nvl(fs.bytes,0))/1024/1024 as BIGGEST   from user_free_space fs, user_tablespaces ts   where fs.tablespace_name(+) = ts.tablespace_name and ts.tablespace_name='RECLAIMTS'   group by ts.tablespace_name;   TABLESPACE_NAME              MB_FREE  FRAGMENTS    BIGGEST ------------------------- ---------- ---------- ---------- RECLAIMTS                   806.5625          5        646 SQL> col segment_type format a10 SQL> set pagesize 100 SQl> select block_id begin_block, block_id + blocks - 1 end_block, blocks, bytes/1024/1024 MB,--表空间扩展使用信息 segment_name, segment_type from dba_extents where tablespace_name = 'RECLAIMTS' union all select block_id begin_block, block_id + blocks - 1 end_block, blocks, bytes/1024/1024 MB, 'free' segment_name, null segment_type from dba_free_space where tablespace_name = 'RECLAIMTS' order by 1 desc; BEGIN_BLOCK END_BLOCK BLOCKS MB SEGMENT_NA SEGMENT_TY ----------- ---------- ---------- ---------- ---------- ---------- 49792 132479 82688 646 free --第一个空闲空间从块49792开始 48768 49791 1024 8 T2 TABLE 47744 48767 1024 8 T2 TABLE 46720 47743 1024 8 T2 TABLE 45696 46719 1024 8 T2 TABLE 44672 45695 1024 8 T2 TABLE 43648 44671 1024 8 T2 TABLE 42624 43647 1024 8 T2 TABLE 41600 42623 1024 8 T2 TABLE 40576 41599 1024 8 T2 TABLE 39552 40575 1024 8 T2 TABLE ... 29312 29319 8 .0625 T2 TABLE 9216 29311 20096 157 free --第二个可以看到的空闲空间从9216开始 1024 1279 256 2 free 1016 1023 8 .0625 T2 TABLE 1008 1015 8 .0625 T2 TABLE ...
3) 实际收缩前,先执行一下分析操作
SQL> set linesize window SQL> variable result clob SQL> col result format a200 SQL> execute DBMS_SPACE.SHRINK_TABLESPACE('RECLAIMTS', SHRINK_MODE => DBMS_SPACE.TS_MODE_ANALYZE, SHRINK_RESULT => :result); PL/SQL procedure successfully completed. SQL> print result RESULT ---------------------------------------------------------------------------------- Movable Objects: 1. { RECLAIM_USER.T2 | type: TABLE | blocks: 20480 | tablespace_name: RECLAIMTS } 2. { RECLAIM_USER.T2_PK | type: INDEX | blocks: 32 | tablespace_name: RECLAIMTS } Total Movable Objects: 2 Total Movable Size(GB): .16 Original Datafile Size(GB): 1.01 Suggested Target Size(GB): .38 Process Time: +00 00:00:01.669704

可移动的对象是表T2极其索引,这时表空间里仅剩的两个对象了。

4)执行收缩操作

更改一下操作模式参数即可

SQL> set serveroutput on SQL> execute dbms_space.shrink_tablespace('RECLAIMTS'); -------------------SHRINK RESULT------------------- Total Moved Objects: 2 Total Moved Size(GB): .16 Original Datafile Size(GB): 1.01 New Datafile Size(GB): .39 Process Time: +00 00:00:10.611905 PL/SQL procedure successfully completed.

收缩操作执行完成,新的数据文件大小为0.39GB。检查一下表空间信息

SQL> select ts.tablespace_name, sum(nvl(fs.bytes,0))/1024/1024 as MB_FREE,      count(*) as FRAGMENTS, max(nvl(fs.bytes,0))/1024/1024 as BIGGEST   from user_free_space fs, user_tablespaces ts      where fs.tablespace_name(+) = ts.tablespace_name and ts.tablespace_name='RECLAIMTS'      group by ts.tablespace_name;    TABLESPACE_NAME              MB_FREE  FRAGMENTS    BIGGEST ------------------------- ---------- ---------- ---------- RECLAIMTS                   168.5625          5        167

表空间的空闲空间和最大空闲空间段大小都减小了。再看一下表空间的扩展分布

SQL> col segment_type format a10 SQL> set pagesize 100 SQl> select block_id begin_block, block_id + blocks - 1 end_block, blocks, bytes/1024/1024 MB,      segment_name, segment_type from dba_extents where tablespace_name = 'RECLAIMTS' union all select block_id begin_block, block_id + blocks - 1 end_block, blocks, bytes/1024/1024 MB,      'free' segment_name, null segment_type from dba_free_space where tablespace_name = 'RECLAIMTS' order by 1 desc; BEGIN_BLOCK  END_BLOCK     BLOCKS         MB SEGMENT_NA SEGMENT_TY ----------- ---------- ---------- ---------- ---------- ----------       49792      50815       1024          8 T2         TABLE       28416      49791      21376        167 free        --可以用来移动数据库对象       27392      28415       1024          8 T2         TABLE       26368      27391       1024          8 T2         TABLE       25344      26367       1024          8 T2         TABLE       24320      25343       1024          8 T2         TABLE       23296      24319       1024          8 T2         TABLE       22272      23295       1024          8 T2         TABLE       21248      22271       1024          8 T2         TABLE       20224      21247       1024          8 T2         TABLE       19200      20223       1024          8 T2         TABLE ...      1056       1151         96        .75 free        --空闲空间        1048       1055          8      .0625 T2_PK      INDEX        1040       1047          8      .0625 T2_PK      INDEX ...
5) 再次分析一下,还有进一步收缩的可能吗?
SQL> variable result clob SQL> col result format a200 SQL> execute DBMS_SPACE.SHRINK_TABLESPACE('RECLAIMTS', SHRINK_MODE => DBMS_SPACE.TS_MODE_ANALYZE, SHRINK_RESULT => :result); SQL> print result RESULT --------------------------------------------------------------------------------- Movable Objects: 1. { RECLAIM_USER.T2 | type: TABLE | blocks: 20480 | tablespace_name: RECLAIMTS } 2. { RECLAIM_USER.T2_PK | type: INDEX | blocks: 32 | tablespace_name: RECLAIMTS } Total Movable Objects: 2 Total Movable Size(GB): .16 Original Datafile Size(GB): .39 Suggested Target Size(GB): .38 Process Time: +00 00:00:01.674684

报告显示,还有收缩的可能

6) 再次运行收缩操作
SQL> set serveroutput on; SQL> execute dbms_space.shrink_tablespace('RECLAIMTS'); -------------------SHRINK RESULT------------------- Total Moved Objects: 2 Total Moved Size(GB): .16 Original Datafile Size(GB): .39 New Datafile Size(GB): .37 Process Time: +00 00:00:11.726847 PL/SQL procedure successfully completed.

执行完毕,数据文件大小缩小到0.37G,看一下表空间信息

SQL> select ts.tablespace_name, sum(nvl(fs.bytes,0))/1024/1024 as MB_FREE,       count(*) as FRAGMENTS, max(nvl(fs.bytes,0))/1024/1024 as BIGGEST     from user_free_space fs, user_tablespaces ts     where fs.tablespace_name(+) = ts.tablespace_name and ts.tablespace_name='RECLAIMTS'     group by ts.tablespace_name; TABLESPACE_NAME         MB_FREE  FRAGMENTS    BIGGEST -------------------- ---------- ---------- ---------- RECLAIMTS              152.5625          5        150

空闲空间减小为152.5625MB,再看一下表空间的扩展分布

SQL> select block_id begin_block, block_id + blocks - 1 end_block, blocks, bytes/1024/1024 MB,         segment_name, segment_type from dba_extents where tablespace_name = 'RECLAIMTS' union all select block_id begin_block, block_id + blocks - 1 end_block, blocks, bytes/1024/1024 MB,         'free' segment_name, null segment_type from dba_free_space where tablespace_name = 'RECLAIMTS' order by 1 desc; BEGIN_BLOCK  END_BLOCK     BLOCKS         MB SEGMENT_NA SEGMENT_TYPE ----------- ---------- ---------- ---------- ---------- --------------------       47744      48767       1024          8 T2         TABLE       46720      47743       1024          8 T2         TABLE         45696      46719       1024          8 T2         TABLE         44672      45695       1024          8 T2         TABLE         43648      44671       1024          8 T2         TABLE         42624      43647       1024          8 T2         TABLE         41600      42623       1024          8 T2         TABLE ...   28416      28543        128          1 T2         TABLE           9216      28415      19200        150 free <= first free space...

同前面的扩展分布信息对比一下,发现数据块28416至49791的空闲空间内被表移动来的表T2的数据占据了。

5 总结及注意事项

从上面的操作可以看出,Oracle 23AI的表空间收缩操作使用起来还是比较简单的,要注意的一是再执行实际收缩操作之前先执行分析操作,预估一下操作的效果后再决定是否执行。二是表空间的收缩操作涉及到数据对象的移动,如果数据对象较大,移动的数据过多会造成大量的IO操作,应该选择系统负载有空余的时间进行操作。

最后修改时间:2025-11-20 13:34:48
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论