暂无图片
暂无图片
17
暂无图片
暂无图片
5
暂无图片

运维日记|我该如何高效清理Oracle大表历史数据

新运维新数据 2021-09-03
5909

各位新朋友~记得先点蓝字关注我哦~


如何清理大表

客户:我们现在有一张上亿行数据的表,里面包含很多历史数据,现在想清理,有没有什么高效的方法进行删除?


此时小编脑海中还闪过n种关键词。

    #$% truncate #$% delete nologging @#$#% delete parallel $#$%^$ delete by rowid %$%^#drop database.¥&%
    复制


    想必大家有会遇到类似场景,如何高效清理大表历史数据?如何高效对大表大量数据进行DML操作?

    这里小编就不针对分区表了,如果是分区表,直接truncate历史分区即可。一张普通大表,如何做到高效清理数据或者更改数据呐?


    常用方法

    下面可能是我们常用的方法:

    1、传统方式直接delete,写个存储过程,10000行提交一次,这种方式往往比较慢并行不易实现

    2、构造历史数据行rowid,根据rowid 高效删除数据,这种方式往往会比较高效但并行不易控制构造较多的无用块(常常可以满足需求)。示例如下:

      declare
      cursor cur_rowid is
      select dbms_rowid.rowid_create(1,
      b.data_object_id,
      a.relative_fno,
      a.block_id,
      0) begin_rowid,
      dbms_rowid.rowid_create(1,
      b.data_object_id,
      a.relative_fno,
      a.block_id + blocks - 1,
      999) end_rowid
      from dba_extents a, dba_objects b
      where a.segment_name = b.object_name
      and a.owner = b.owner
      and b.object_name = 'OB3'
      and b.owner = 'JOE'
      order by a.relative_fno, a.block_id;
      r_sql varchar2(4000);
      begin
      FOR cur in cur_rowid LOOP
      r_sql := 'delete OB3 where object_type =' || '''' || 'SYNONYM' || '''' ||
      ' and rowid between :1 and :2';
      EXECUTE IMMEDIATE r_sql
      using cur.begin_rowid, cur.end_rowid;
      COMMIT;
      END LOOP;
      end;
      /
      复制


      3、使用DBMS_PARALLEL_EXECUTE包进行删除,这个也就我们今天的主角。我们常说客户是上帝,有求必应,ORACLE也不例外。针对客户的大量需求,11GR2 新出的 DBMS_PARALLEL_EXECUTE包,能帮助我们高效对大表进行DML操作。在我们需要清理一些大表的历史数据的时候,这个也无疑是我们较好的工具。话不多时,上才艺,EGM...

        SET SERVEROUTPUT ON
        BEGIN
        DBMS_PARALLEL_EXECUTE.DROP_TASK ('test_task');
        EXCEPTION WHEN OTHERS THEN
        NULL;
        END;
        /


        DECLARE
        l_task VARCHAR2(30) := 'test_task';
        l_sql_stmt VARCHAR2(32767);
        l_try NUMBER;
        l_status NUMBER;
        BEGIN
        -- Create the TASK
        DBMS_PARALLEL_EXECUTE.CREATE_TASK (task_name => l_task);
        -- Chunk the table by the ROWID
        DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_ROWID
        (
        TASK_NAME => l_task,
        TABLE_OWNER => 'JOE', <<<用户名
        TABLE_NAME => 'OB2', <<<表名
        BY_ROW => TRUE, <<<表示chunk_size为行数,否则表示块数
        CHUNK_SIZE => 2500 <<<自定义chunk size,这里表示2500行为一个chunk
        );
        -- DML to be execute in parallel
        l_sql_stmt := 'delete OB2 where object_type = ''SYNONYM'' and rowid BETWEEN :start_id AND :end_id'; <<<想要执行的SQL语句
        -- Run the task
        DBMS_PARALLEL_EXECUTE.RUN_TASK
        (
        TASK_NAME => l_task,
        SQL_STMT => l_sql_stmt,
        LANGUAGE_FLAG => DBMS_SQL.NATIVE,
        PARALLEL_LEVEL => 2 <<<自定义执行并行度
        );
        -- If there is error, RESUME it for at most 2 times.
        l_try := 0;
        l_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS(l_task);
        WHILE(l_try < 2 and l_status != DBMS_PARALLEL_EXECUTE.FINISHED)
        LOOP
        l_try := l_try + 1;
        DBMS_PARALLEL_EXECUTE.RESUME_TASK(l_task);
        l_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS(l_task);
        END LOOP;
        -- Done with processing; drop the task
        DBMS_PARALLEL_EXECUTE.DROP_TASK(l_task);
        EXCEPTION WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Error in the code :' || SQLERRM);
        END;
        /
        复制


        如上述脚本,使用方法较为简单,修改<<<备注部分,即可进行执行。以上这个脚本是通过rowid切割表的方式进行分片,当然切割表的方法其还提供两种。

        一是通过指定字段:CREATE_CHUNKS_BY_NUMBER_COL。

        二是通过自己指定sql来切割:CREATE_CHUNKS_BY_SQL,这里不一一说明。


        基本原理是将一个大表以指定chunk size进行分片(chunk size 可以指定行数或者块数),然后并行对多个分片进行delete 或者其他DML操作,每一分片完成后立即提交,通过调用job进行并发控制操作。


        所以,想要调用DBMS_PARALLEL_EXECUTE包,除了拥有此包的访问权限之外,还必须要拥有create job权限

        其基本执行流程如下:

        1、调用create_task(),创建任务task。

        2、调用create_chunk_by_rowid(),创建分块规则。

        3、编写自己需要执行的DML操作语句。

        4、调用run_task(),运行task任务。

        5、调用drop_task()过程,即任务执行完成后,删除任务。


        涉及相关视图如下:

          DBA_PARALLEL_EXECUTE_TASKS
          DBA_PARALLEL_EXECUTE_CHUNKS
          dba_scheduler_jobs
          复制


          在任务执行过程中,可以通过以上视图,实时监控task 的执行情况。

          参考文献

          参考文档:Database PL/SQL Packages and Types Reference


          上期的免费送书活动还在进行中哦!截止日期周五上午10点,请大家踊跃参与!

          并将朋友圈点赞截图发送至新运维新数据公众号哦~

          【福利】免费送书活动!!

          美创运维中心数据库服务团队拥有Oracle ACE 1人、OCM 10余人、数十名Oracle OCP、MySQL OCP、红帽RHCA、中间件weblogic、tuxedo认证、达梦工程师 ,著有《Oracle DBA实战攻略》,《Oracle数据库性能优化方法和最佳实践》,《Oracle内核技术揭秘》等多本数据运维优化书籍。目前运维各类数据库合计2000余套,精通Oracle、MySQL、SQLServer、DB2、PostgreSQL、达梦等主流商业和开源数据库。并成为首批国内达梦战略合作伙伴之一,拥有海量经验和完善的人员培养体系。并同时提供超融合,私有云整体解决方案。



          文章转载自新运维新数据,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论

          黑黑
          暂无图片
          5月前
          评论
          暂无图片 0
          5月前
          暂无图片 点赞
          评论
          筱悦星辰
          暂无图片
          1年前
          评论
          暂无图片 0
          任何反常的、超预期的、难以理解的事件,先别急着排斥,因为这是我们调整自己对客观世界认知的好机会。
          1年前
          暂无图片 点赞
          评论
          老钢炮
          暂无图片
          2年前
          评论
          暂无图片 0
          咨询个问题,如果是10T以上的分区表,批量清理历史分区的数据,建议是用什么方式呢,1、tuncate + drop update global indexes;2、直接drop partition update global indexes; 貌似这两种都会影响业务实时写入数据,严重的可能会造成ORA-03113: end-of-file on communication channel 请问有啥更好的办法么
          2年前
          暂无图片 点赞
          1
          有问题吗?
          暂无图片
          2年前
          回复
          暂无图片 0
          大佬,10TB的表用Global 索引,Global 索引的Blevel 是几? 这样改让开发改业务用Local 索引。
          2年前
          暂无图片 点赞
          回复
          灰色轨迹
          暂无图片
          3年前
          评论
          暂无图片 0
          学习了
          3年前
          暂无图片 点赞
          评论