各位新朋友~记得先点蓝字关注我哦~
如何清理大表
客户:我们现在有一张上亿行数据的表,里面包含很多历史数据,现在想清理,有没有什么高效的方法进行删除?
此时小编脑海中还闪过n种关键词。
#$% truncate #$% delete nologging @#$#% delete parallel $#$%^$ delete by rowid %$%^#drop database.¥&%
想必大家有会遇到类似场景,如何高效清理大表历史数据?如何高效对大表大量数据进行DML操作?
这里小编就不针对分区表了,如果是分区表,直接truncate历史分区即可。一张普通大表,如何做到高效清理数据或者更改数据呐?
常用方法
下面可能是我们常用的方法:
1、传统方式直接delete,写个存储过程,10000行提交一次,这种方式往往比较慢,并行不易实现。
2、构造历史数据行rowid,根据rowid 高效删除数据,这种方式往往会比较高效,但并行不易控制,构造较多的无用块(常常可以满足需求)。示例如下:
declarecursor cur_rowid isselect 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_rowidfrom dba_extents a, dba_objects bwhere a.segment_name = b.object_nameand a.owner = b.ownerand b.object_name = 'OB3'and b.owner = 'JOE'order by a.relative_fno, a.block_id;r_sql varchar2(4000);beginFOR cur in cur_rowid LOOPr_sql := 'delete OB3 where object_type =' || '''' || 'SYNONYM' || '''' ||' and rowid between :1 and :2';EXECUTE IMMEDIATE r_sqlusing cur.begin_rowid, cur.end_rowid;COMMIT;END LOOP;end;/
3、使用DBMS_PARALLEL_EXECUTE包进行删除,这个也就我们今天的主角。我们常说客户是上帝,有求必应,ORACLE也不例外。针对客户的大量需求,11GR2 新出的 DBMS_PARALLEL_EXECUTE包,能帮助我们高效对大表进行DML操作。在我们需要清理一些大表的历史数据的时候,这个也无疑是我们较好的工具。话不多时,上才艺,EGM...
SET SERVEROUTPUT ONBEGINDBMS_PARALLEL_EXECUTE.DROP_TASK ('test_task');EXCEPTION WHEN OTHERS THENNULL;END;/DECLAREl_task VARCHAR2(30) := 'test_task';l_sql_stmt VARCHAR2(32767);l_try NUMBER;l_status NUMBER;BEGIN-- Create the TASKDBMS_PARALLEL_EXECUTE.CREATE_TASK (task_name => l_task);-- Chunk the table by the ROWIDDBMS_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 parallell_sql_stmt := 'delete OB2 where object_type = ''SYNONYM'' and rowid BETWEEN :start_id AND :end_id'; <<<想要执行的SQL语句-- Run the taskDBMS_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)LOOPl_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 taskDBMS_PARALLEL_EXECUTE.DROP_TASK(l_task);EXCEPTION WHEN OTHERS THENDBMS_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_TASKSDBA_PARALLEL_EXECUTE_CHUNKSdba_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、达梦等主流商业和开源数据库。并成为首批国内达梦战略合作伙伴之一,拥有海量经验和完善的人员培养体系。并同时提供超融合,私有云整体解决方案。







