ITPub博客

首页 > 数据库 > Oracle > Oracle如何影响最低地回收段空间?

Oracle如何影响最低地回收段空间?

原创 Oracle 作者:a960549548 时间:2026-11-11 16:04:15 0 删除 编辑

前面的文章里用 DBMS_SPACE 判断过"段该不该收缩":满块寥寥、fs4 空块成堆,结论是收缩能收回大量空间。但真到动手,常规手段各有痛点:ALTER TABLE MOVE 会锁表、索引全部失效要重建;SHRINK 虽然在线,但长事务下反复搬行,大表上耗时可观,而且要求开行迁移。对于不能停业务的表,第三条路是在线重定义:DBMS_REDEFINITION 包。

我的思路是:建一张结构符合预期的新表(中间表),程序自动把原表数据灌进去,并在原表上建物化视图日志跟踪重定义期间的增量修改;期间业务对原表的读写完全正常。一切就绪后执行 FINISH,把两张表的名字在数据字典里对调——原表瞬间"变成"了新表。整个过程只有 FINISH 那一刻需要短暂的排他锁,通常以秒计。

顺带的能力不止搬表空间:换存储参数、压缩、改列类型、拆分区、加删列(通过列映射),都可以在一次重定义里完成。

一、程序用法介绍


-- 检查表是否具备在线重定义资格(不合格直接报错)

DBMS_REDEFINITION .CAN_REDEF_TABLE (
  uname         IN VARCHAR2 ,
  tname         IN VARCHAR2 ,
  options_flag IN PLS_INTEGER : = 1 ) ;   -- 1: CONS_USE_PK  2: CONS_USE_ROWID

-- 开始重定义:灌数据 + 建跟踪日志
DBMS_REDEFINITION .START_REDEF_TABLE (
  uname         IN VARCHAR2 ,
  orig_table   IN VARCHAR2 ,
  int_table     IN VARCHAR2 ,
  col_mapping   IN VARCHAR2 : = NULL ,
  options_flag IN BINARY_INTEGER : = 1 ) ;

-- 复制索引、约束、触发器、权限等从属对象
DBMS_REDEFINITION .COPY_TABLE_DEPENDENTS (
  uname             IN  VARCHAR2 ,
  orig_table       IN  VARCHAR2 ,
  int_table         IN  VARCHAR2 ,
  copy_indexes     IN  PLS_INTEGER : = 1 ,       -- 1: 按原索引物理参数复制
  copy_triggers     IN   BOOLEAN     : = TRUE ,
  copy_constraints IN   BOOLEAN     : = TRUE ,
  copy_privileges   IN   BOOLEAN     : = TRUE ,
  ignore_errors     IN   BOOLEAN     : = FALSE ,
  num_errors       OUT PLS_INTEGER ,
  copy_statistics   IN   BOOLEAN     : = FALSE ) ;

-- 可选:把增量同步到中间表,缩短 FINISH 等待
DBMS_REDEFINITION .SYNC_INTERIM_TABLE (
  uname , orig_table , int_table , ... ) ;

-- 收尾切换
DBMS_REDEFINITION .FINISH_REDEF_TABLE (
  uname , orig_table , int_table , ... ) ;

-- 放弃重定义,清理过程对象
DBMS_REDEFINITION .ABORT_REDEF_TABLE (
  uname , orig_table , int_table , ... ) ;

两个跟踪模式:CONS_USE_PK(默认)按主键识别行变化,要求表有主键(或全列非空的唯一键);无主键的表用 CONS_USE_ROWID,限制多一些。col_mapping 为 NULL 时按同名列全量对应,需要改造列时在这里写映射表达式 。

二、实测:把表在线搬到新表空间

沿用 SMALL_TBS 那个小表空间,把一张表在线搬到 USERS 表空间,业务全程不停。

1. 授权

用户模式重定义自己 schema 的表,需要 CREATE TABLE 和 CREATE MVIEW 权限:


SQL
> 
grant 
create 
table
, 
create mview 
to test
;

SQL > grant execute on dbms_redefinition to test ;
SQL > alter user test quota unlimited on users ;

Grant succeeded.

2. 准备原表(TEST 用户)


SQL
> 
create 
table t_redef
(id number 
primary 
key
, pad varchar2
(
100
))

  2   tablespace small_tbs ;

Table created.

SQL > insert into t_redef select rownum , rpad ( 'x' , 100 , 'x' )
  2   from dual connect by level <= 10000 ;

10000 rows created.

SQL > commit ;

Commit complete.

SQL > create index idx_t_redef on t_redef (pad ) ;

Index created.

3. 检查资格


SQL
> 
begin

  2    dbms_redefinition .can_redef_table ( 'TEST' , 'T_REDEF' ) ;
  3    dbms_output .put_line ( 'ok: 候选表' ) ;
  4   end ;
  5  /
ok : 候选表

PL/SQL procedure successfully completed.

有主键,默认的 CONS_USE_PK 模式即可。不合格时这一步会直接报错并说明原因(比如主键缺失、表类型不支持),比 START 之后才发现问题好得多。

4. 建中间表并启动


SQL
> 
create 
table t_redef_new 
tablespace users

  2   as select * from t_redef where 1 = 0 ;

Table created.

SQL > begin
  2    dbms_redefinition .start_redef_table (
  3      uname       => 'TEST' ,
  4      orig_table => 'T_REDEF' ,
  5      int_table   => 'T_REDEF_NEW' ) ;
  6   end ;
  7  /

PL/SQL procedure successfully completed.

中间表只要结构匹配即可,用 CTAS 空拷贝最省事。START 执行两件事:把现有数据灌入中间表(这一步对大表是主要的 IO 开销,安排低峰);在原表上创建物化视图日志,此后业务对原表的 DML 都被记录。此时另一会话可以继续对 T_REDEF 正常增删改。

5. 复制从属对象


SQL
> 
set serveroutput 
on

SQL > declare
  2    l_errs number ;
  3   begin
  4    dbms_redefinition .copy_table_dependents (
  5      uname             => 'TEST' ,
  6      orig_table       => 'T_REDEF' ,
  7      int_table         => 'T_REDEF_NEW' ,
  8      copy_indexes     => 1 ,
  9      copy_triggers     => true ,
10      copy_constraints => true ,
11      copy_privileges   => true ,
12      ignore_errors     => true ,
13      num_errors       => l_errs ) ;
14    dbms_output .put_line ( 'errors = ' || l_errs ) ;
15   end ;
16  /
errors = 0

PL/SQL procedure successfully completed.

ignore_errors => TRUE 让复制过程不被单个对象失败打断,错误计数与明细记在 DBA_REDEF_ERRORS 里,事后核查。主键约束、普通索引、权限都到了中间表上 。

6. 收尾切换


SQL
> exec dbms_redefinition
.finish_redef_table
(
'TEST'
, 
'T_REDEF'
, 
'T_REDEF_NEW'
)


PL/SQL procedure successfully completed.

FINISH 把 START 之后的增量同步过来,然后在数据字典里把两张表对调。最后那一刻需要短暂排他锁,等待 DML 结束即可,通常瞬间完成。如果窗口期想更短,可在 FINISH 前先执行一次 SYNC_INTERIM_TABLE 预同步 。

7. 验证与清理


SQL
> 
select tablespace_name 
from dba_tables

  2   where owner = 'TEST' and table_name = 'T_REDEF' ;

TABLESPACE_NAME
------------------------------
USERS

SQL > drop table t_redef_new purge ;

Table dropped.

表已经在 USERS 里,业务侧无感。注意两点:索引随 copy_indexes => 1 按原物理参数复制,若希望索引也落到新表空间,复制后对中间表索引 ALTER INDEX ... REBUILD TABLESPACE ... 或手工重建;切换完成后中间表名的表实际是旧段,确认无误后删除回收空间 。

三、注意事项

  1. 重定义期间在原表上自动创建物化视图日志,异常中断后用 ABORT_REDEF_TABLE 清理过程对象,不要直接删中间表了事;

  2. 克隆过来的触发器在重定义完成前不会触发,克隆的外键约束生成时是 DISABLED 状态,FINISH 后要检查 DBA_CONSTRAINTS 并手工启用,这是最容易漏的两件事;

  3. 大表的初始灌数据阶段 IO、undo 开销不亚于一次 CREATE TABLE AS SELECT,务必低峰执行,并确认 undo 与中间表所在表空间有余量;

  4. 无主键表用 CONS_USE_ROWID,但该模式不支持修改列映射等改造场景,且重定义期间原表不能做直接路径类操作;

  5. FINISH 的短暂排他锁若遇长事务会等待,12c 起可用 dml_lock_timeout 参数设上限,超时失败再重来,避免无限等;

  6. 每张表同一时间只能有一个进行中的重定义;操作前 CAN_REDEF_TABLE、操作后查 DBA_REDEF_ERRORS,一前一后两个检查点不要省 。

对比一下三条收缩路线的取舍:MOVE 最简单但锁表、索引失效;SHRINK 在线但大表耗时长;在线重定义步骤最多,但业务影响最小,还能顺带完成压缩、分区等结构改造。表越大、业务越忙,越值得用最后这一条。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013625/viewspace-3188623/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
Oracle ACE,MySQL 8.0 OCP,PMP,拥有OceanBase OBCP,TiDB PCTP,GoldenDB GDCP等多项认证,ITPUB认证专家,微信公众号:DBA之友。

注册时间:2022-02-08

  • 博文量
    136
  • 访问量
    175971