各位新朋友~记得先点蓝字关注我哦~
如何清理大表
客户:我们现在有一张上亿行数据的表,里面包含很多历史数据,现在想清理,有没有什么高效的方法进行删除?
此时小编脑海中还闪过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、达梦等主流商业和开源数据库。并成为首批国内达梦战略合作伙伴之一,拥有海量经验和完善的人员培养体系。并同时提供超融合,私有云整体解决方案。

评论




