多条件删除数据_Oracle 大数据表清理迁移方案

背景

为了应对系统投产两年多时间内,数据库中数据量不断增多。系统运行效率性能在海量不相关历史数据的影响下有下降风险。为提高系统运行效率,定时将有效期外(例如一年)的数据视为历史数据,迁移至历史表空间,同时清除当前库数据,或者采用数据归档入数仓后直接删除。

方案目的

  • 历史数据完整迁移
  • 正式库数据安全删除
  • 迁移所涉及表可配置(增量、全量)
  • 可手工操作、可定时任务(JOB),对于操作结果需要记录,方便审计及查询记录

方案概要

整个方案通过将源表(需要做数据清理的表)满足迁移条件的数据移动到历史表的方式来清理大表数据,所有历史表建立统一的历史表空间和用户。同时为了满足通用性,采用配置的方式来配置需要迁移的表及清理条件。同时将清理的结果记录到清理日志表。方便事后完整性验证。

迁移方式采用job+存储过程形式进行,由job定时拉起存储过程进行数据迁移,第一次迁移时采用手动的方式,此后每日增量数据采用定时任务进行自动清理。

迁移逻辑

  1. 建立历史用户和历史表空间,赋予历史用户对源表的SELECT、DELETE操作权限。这个工作可以在清理前完成,作为前置条件
select 'GRANT ALL ON  源表用户.'||table_name||' to 清理历史用户;'  from all_tables where lower(owner)='源表用户';
  1. 在历史表空间中创建需要迁移的历史表(分区表,方便历史表归档),表名规则为,源表名_BAK
  2. 在历史表空间中创建迁移配置表和清理日志表 迁移配置表:迁移配置表中一条记录就是一张表清理条件,包含如下一些字段

ad3db39a81b7f5283ae64406030316d1.png

将需要清理表的数据插入到该表。

清理日志表:清理日志表记录清理的结果,方便查询历史清理情况及做完整性校验

8f0e775cdfff1862a9234cecb1d21c32.png

将每张表的清理情况记录到该表中

4. 查询迁移配置表执行清理。迁移配置表包括源表名、目标表名(也就是历史表名,规则为在源表名后面加_bak)、迁移条件(筛选清理数据条件)、迁移类型(全量、增量)、每次处理页大小(分页和批量提交大小)、清理标志位(根据筛选清理条件物理删除数据)

5. 数据迁移和清理,需要保证在同一个事务中,通过控制page_size大小控制事务的大小

  • 如果“清理标志位”字段为2,直接跳过
  • 如果是全量删除,需要先清空历史表
  • 使用历史用户根据迁移条件分页查询源表数据
  • 按page_size将数据插入历史表
  • 根据“清理标志位”字段判断进行源表清理page_size条数据,跟插入在同一个事务中

6. 删除数据逻辑

  • 非分区表:根据要删除的表清理条件查询出rowid,删除时根据rowid+表清理条件作为删除条件进行删除分批提交;
--示例代码
declare  
    --按ROWID排序的Cursor,删除条件是XXX=XXXX,根据实际情况来定。
   cursor mycursor is SELECT  主键或唯一键列名 FROM 源表 WHERE  XXX=XXXX  order by rowid;   
   v_del_conditon xx;
BEGIN
   open mycursor;
   loop
     --每次处理5000行,也就是每5000行一提交
     fetch   mycursor bulk collect into v_del_conditon  limit 5000;  
     exit when v_del_conditon.count=0;
     forall i in v_rowid.first..v_rowid.last
        delete from test where rowid = v_del_conditon (i) and XXX=XXXX(此处为上面的查询条件);
     commit;
  • 分区表:根据分区初始日期-数据保留天数得出要删除的分区区间,然后查询出所有分区,如果判断该分区在此区间则进行删除
--示例代码
 for x in (select * from dba_tab_partitions where       table_name=upper(tab_name)) loop
      /** 将分区的high_value赋值到partition_date */
      execute immediate 'select '|| x.high_value || 'from dual' into partition_date;
      /** partition_date在开始时间和结束时间之间的分区drop掉,并更新全局索引*/
      if cur_date < partition_date and partition_date <= end_date then
          v_drop_sql := 'ALTER TABLE ' || orig_table_owner || '.' || tab_name || ' DROP PARTITION ' || x.partition_name || ' update global indexes';
          execute immediate v_drop_sql;
       end if;
    end loop;

以上方案完整脚本通过关注公众号「陈言懒调」发送“大表清理”获取;脚本中包含了创建表、创建清理存储过程、创建公共函数(打印日志)等。

总结

上述方案看似简单,实则经历了很长一段的优化调整,最终形成此方案,调整过程中包括以下几点

  • 使用forall方式删除代替原有for的方式将删除性能提升百倍
  • 使用rowid删除时原本以为直接使用rowid即可,后发现如果一边删除一边写入的话会造成误删数据的情况
  • 分区表删除数据并不会释放空间,后采用直接删除分区形式
  • 一开始采用数据库调用存储过程方式多改成Java多线程调用再次提升性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值