当前位置 博文首页 > LuciferLiu_DBA:实战篇:Oracle分区表必知必会【在线重定义】

    LuciferLiu_DBA:实战篇:Oracle分区表必知必会【在线重定义】

    作者:[db:作者] 时间:2021-08-06 15:59

    作者简介

    • 作者:LuciferLiu,中国DBA联盟(ACDU)成员。
    • 目前主要从事Oracle DBA工作,曾从事 Oracle 数据库开发工作,主要服务于生产制造,汽车金融等行业。
    • 现拥有Oracle OCP,OceanBase OBCA认证,擅长Oracle数据库运维开发,备份恢复,安装迁移,Linux自动化运维脚本编写等。

    在这里插入图片描述

    前言

    为什么要普通表转分区表?有哪些方式可以做?

    • 分区表作为Oracle三大组件之一,在Oracle数据库中,起着至关重要的作用。

    分区表有什么优点?

    • 普通表转分区表:应用程序无感知,DML 语句无需修改即可访问分区表。
    • 高可用性:部分分区不可用不影响整个分区表使用。
    • 方便管理:可以单独对分区进行DDL操作,列入重建索引或扩展分区,不影响分区表的使用。
    • 减少OLTP系统资源争用:因为DML分布在很多段上进行操作。

    使用在线重定义的方式进行分区表的转换,优势在于可以在线进行,流程简单,可以快速进行转换。

    一、介绍

    DBMS_REDEFINITION(在线重定义):

    • 支持的数据库版本:Oracle Database - Enterprise Edition - Version 9.2.0.4 and later
    • 在线重定义是通过 物化视图 实现的。

    使用在线重定义的一些限制条件

    • 必须有足够的表空间来容纳表的两倍数据量。
    • 主键列不能被修改。
    • 表必须有主键。
    • 必须在同一个用户下进行在线重定义。
    • SYS和SYSTEM用户下的表无法进行在线重定义。
    • 在线重定义无法采用nologging。
    • 如果中间表有新增列,则不能有NOT NULL约束

    DBMS_REDEFINITION包:

    • ABSORT_REDEF_TABLE:清理重定义的错误和中止重定义;
    • CAN_REDEF_TABLE:检查表是否可以进行重定义,存储过程执行成功代表可以进行重定义;
    • COPY_TABLE_DEPENDENTS:同步索引和依赖的对象(包括索引、约束、触发器、权限等);
    • FINISH_REDEF_TABLE:完成在线重定义;
    • REGISTER_DEPENDENTS_OBJECTS:注册依赖的对象,如索引、约束、触发器等;
    • START_REDEF_TABLE:开始在线重定义;
    • SYNC_INTERIM_TABLE:增量同步数据;
    • UNREGISTER_DEPENDENT_OBJECT:不注册依赖的对象,如索引、约束、触发器等;

    二、实战

    测试环境数据库安装:

    • 11G:./OracleShellInstall.sh -i 10.211.55.111

    更多更详细的脚本使用方式可以订阅专栏:Oracle一键安装脚本。

    脚本获取方式:

    • GitHub 持续保持更新中🔥
    • Gitee 持续保持更新中🔥

    1 构建测试数据

    创建测试表空间和用户:

    sqlplus / as sysdba
    create tablespace PAR;
    create user par identified by par;
    grant dba to par;
    

    在这里插入图片描述
    创建测试表:

    sqlplus par/par
    create table lucifer(
    id number(8) PRIMARY KEY,
    name varchar2(20) not null,
    par_date date)
    tablespace PAR;
    comment on table lucifer is 'lucifer表';
    comment on column lucifer.name is '姓名';
    comment on column lucifer.par_date is '分区日期';
    create index id_name on lucifer(name) tablespace par;
    

    在这里插入图片描述
    插入测试数据:

    sqlplus par/par
    begin
      for i in 0 .. 24 loop
        insert into lucifer values
          (i,
           'lcuifer_' || i,
           add_months(to_date('2021-1-1', 'yyyy-mm-dd'), i));
      end loop;
      commit;
    end;
    /
    

    在这里插入图片描述
    可以看到,测试数据已经构建完成,接下来开始实战操作。

    2 查看是否能够重定义

    需提前确认表是否有主键,表空间是否足够:

    sqlplus / as sysdba
    ##查看主键
    select cu.* from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'P' and au.table_name = 'LUCIFER';
    

    在这里插入图片描述
    确认是否可以重定义,没有主键用rowid:

    sqlplus / as sysdba
    exec dbms_redefinition.can_redef_table('PAR', 'LUCIFER');
    

    在这里插入图片描述
    执行没有报错代表可以进行表的在线重定义。

    3 创建中间表(分区表结构)

    通过PL/SQL包一键生成分区表结构:

    sqlplus par/par
    BEGIN
      ctas_par(p_tab        => 'lucifer',
               p_part_colum => 'par_date',
               p_part_nums  => 24,
               p_tablespace => 'par');
    END;
    /
    

    在这里插入图片描述
    注意:PL/SQL包可参考:Oracle普通表按月转分区表,通过PLSQL包一键生成分区表

    创建中间分区表lucifer_par:

    create table lucifer_par
    (
      id       NUMBER(8),
      name     VARCHAR2(20),
      par_date DATE
    )
    partition BY RANGE(par_date)(
    partition lucifer_P202101 values less than (TO_DATE(' 2021-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202102 values less than (TO_DATE(' 2021-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202103 values less than (TO_DATE(' 2021-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202104 values less than (TO_DATE(' 2021-05-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202105 values less than (TO_DATE(' 2021-06-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202106 values less than (TO_DATE(' 2021-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202107 values less than (TO_DATE(' 2021-08-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202108 values less than (TO_DATE(' 2021-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202109 values less than (TO_DATE(' 2021-10-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202110 values less than (TO_DATE(' 2021-11-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202111 values less than (TO_DATE(' 2021-12-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202112 values less than (TO_DATE(' 2022-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202201 values less than (TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202202 values less than (TO_DATE(' 2022-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202203 values less than (TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202204 values less than (TO_DATE(' 2022-05-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202205 values less than (TO_DATE(' 2022-06-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202206 values less than (TO_DATE(' 2022-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202207 values less than (TO_DATE(' 2022-08-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202208 values less than (TO_DATE(' 2022-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202209 values less than (TO_DATE(' 2022-10-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202210 values less than (TO_DATE(' 2022-11-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202211 values less than (TO_DATE(' 2022-12-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_P202212 values less than (TO_DATE(' 2023-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) tablespace par,
    partition lucifer_MAX values less than (maxvalue) tablespace par)
                 enable row movement
                 tablespace par;
    

    在这里插入图片描述
    如上,唯一索引和约束不加,会自动复制,分区表结构的中间表已经生成。

    4 检查中间表是否开启行迁移

    select row_movement from dba_tables where table_name='LUCIFER' and owner='PAR';
    select row_movement from dba_tables where table_name='LUCIFER_PAR' and owner='PAR';
    

    在这里插入图片描述

    5 收集表统计信息

    为了确保数据准确,开始前进行统计信息收集:

    sqlplus / as sysdba
    exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8') ;
    exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER_PAR',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8') ;
    

    在这里插入图片描述

    6 开始在线重定义

    sqlplus / as sysdba
    EXEC DBMS_REDEFINITION.START_REDEF_TABLE('PAR','LUCIFER','LUCIFER_PAR');
    

    在这里插入图片描述

    7 复制表属性,排除索引

    选择自动复制表属性,手动创建本地索引(local)

    • 优点:只需要关注索引是否遗漏,无需关注触发器,权限,约束等依赖。
    • 缺点:需要手动创建索引,并且结束后手动rename索引。

    可参考:Oracle在线重定义之COPY_TABLE_DEPENDENTS

    sqlplus par/par
    SET SERVEROUTPUT ON
    DECLARE
      l_errors  NUMBER;
    BEGIN
      DBMS_REDEFINITION.copy_table_dependents(
        uname            => USER,
        orig_table       => 'LUCIFER',
        int_table        => 'LUCIFER_PAR',
        copy_indexes     => 0,
        copy_triggers    => TRUE,
        copy_constraints => TRUE,
        copy_privileges  => TRUE,
        ignore_errors    => FALSE,
        num_errors       => l_errors,
        copy_statistics  => FALSE,
        copy_mvlog       => FALSE);
        
      DBMS_OUTPUT.put_line('Errors=' || l_errors);
    END;
    /
    

    在这里插入图片描述
    执行过程没有任何报错,代表正常。

    8 中间表创建本地索引

    中间表LUCIFER_PAR创建索引:

    create index ID_NAME_PAR on LUCIFER_PAR(NAME) tablespace PAR local parallel 8;
    

    在这里插入图片描述
    注意:索引名称需要与原索引名称不一致。

    9 取消索引并行度

    如果创建索引时,开启并行创建,则需要取消索引并行度:

    sqlplus / as sysdba
    select 'alter index '||owner||'.'||index_name||' noparallel;'
    from dba_indexes 
    where table_name = 'LUCIFER_PAR' and owner= 'PAR';
    

    在这里插入图片描述

    10 同步数据(可以减少结束重定义过程的锁表时间)

    sqlplus / as sysdba
    BEGIN
    dbms_redefinition.sync_interim_table(
    uname => 'PAR',
    orig_table => 'LUCIFER',
    int_table => 'LUCIFER_PAR');
    END;
    /
    

    在这里插入图片描述
    注意:这一步操作是为了在结束重定义的时候,减少锁表的时间。

    11 收集中间表统计信息

    为了下面同步数据做准备,收集中间表统计信息:

    sqlplus / as sysdba
    exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER_PAR',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8'