你可能经常看到,设计为串行执行的应用(一般是批处理应用)往往类似于以下过程:
create or replace procedure serial
is
begin
for x in (select object_id id, object_name text from big_table)
loop
insert into t2 (id, text) values (x.id, x.text);
end loop;
end;
/
在这种情况下,oracle的并行查询或PDML没有什么帮组。如果oracle并行的执行简单的select object_id id, object_name text from big_table,可能不会提供任何显著的速度提升。如果oracle在完成复杂的处理之后再并行的执行update或insert,则没有任何好处,毕竟这里只会update/insert一行。
从oracle11g R2开始,有了一种新的方法可以通过dbms_parallel_execute内置包实现并行化。其实原理也比较简 单:首先是把数据分成一个个CHUNK,然后指定并行度来同时执行这些任务。就类似手工的按照主键、ROWID或者数据的日期等等进行并行处理类似。现在修改serial过程,如下所示:
create or replace procedure serial(
p_lo_rid in rowid, p_hi_rid in rowid)
is
begin
for x in (select object_id id, object_name text from big_table
where rowid between p_lo_rid and p_hi_rid)
loop
insert into t2 (id, text,
session_id) values (x.id, x.text,
sys_context('userenv', 'sessionid'));
end loop;
end;
/
使用dbms_parallel_execute一般分为3个步骤:创建一个TASK;然后创建CHUNK把数据进行分批;最后是执行这个TASK。下面来做一个演示,演示更新一个表的过程:
1、首先创建一个TASK
u1@ORCL> exec dbms_parallel_execute.create_task('process big table');
PL/SQL 过程已成功完成。
u1@ORCL> select task_name,chunk_type,status from dba_parallel_execute_tasks;
TASK_NAME CHUNK_TYPE STATUS
-------------------- ------------ -------------------
process big table UNDELARED CREATED
2、把将要更新的表按照ROWID进行分批,分到各个CHUNK中
u1@ORCL> begin
2 dbms_parallel_execute.create_chunks_by_rowid
3 ( task_name => 'process big table',
4 table_owner => user,
5 table_name => 'BIG_TABLE',
6 by_row => false,
7 chunk_size => 10000 );
8 end;
9 /
PL/SQL 过程已成功完成。
u1@ORCL> select *
2 from (select chunk_id, task_name, status, start_rowid, end_rowid
3 from dba_parallel_execute_chunks
4 where task_name = 'process big table'
5 order by chunk_id)
6 where rownum <= 5;
CHUNK_ID TASK_NAME STATUS START_ROWID END_ROWID
---------- -------------------- -------------------- ------------------ ------------------
1 process big table UNASSIGNED AAAZJSAAEAAAADIAAA AAAZJSAAEAAAADPCcP
2 process big table UNASSIGNED AAAZJSAAEAAAADQAAA AAAZJSAAEAAAADXCcP
3 process big table UNASSIGNED AAAZJSAAEAAAADYAAA AAAZJSAAEAAAADfCcP
4 process big table UNASSIGNED AAAZJSAAEAAAADgAAA AAAZJSAAEAAAADnCcP
5 process big table UNASSIGNED AAAZJSAAEAAAADoAAA AAAZJSAAEAAAADvCcP
BY_ROW:分CHUNK的类型。如果为TRUE,则后面的CHUNK_SIZE表示是行;如果是FALSE,则后面的CHUNK_SIZE表示的是BLOCK。
CHUNK_SIZE:CHUNK大小。如果BY_ROW为TRUE,表示多少行分为一个CHUNK;如果BY_ROW为FALSE,则表示多少块分为一个CHUNK。
3、执行并行任务
u1@ORCL> begin
2 dbms_parallel_execute.run_task
3 ( task_name => 'process big table',
4 sql_stmt => 'begin serial( :start_id, :end_id ); end;',
5 language_flag => DBMS_SQL.NATIVE,
6 parallel_level => 4 );
7 end;
8 /
PL/SQL 过程已成功完成。
在执行过程中可以通过dba_parallel_execute_chunks视图观察执行情况,预估大致完成时间。如果某个chunk失败,可以纠正发生错误的根本原因,其它chunk会继续执行,知道所有chunk都成功完成时,就大功告成了。下面将任务删除:
u1@ORCL> exec dbms_parallel_execute.drop_task('process big table' );
PL/SQL 过程已成功完成。
如果查看我们自己的应用表,可以看到有4个并行作业,其中每个作业处理的行数大致相同:
u1@ORCL> select session_id, count(*)
2 from t2
3 group by session_id
4 order by session_id;
SESSION_ID COUNT(*)
---------- ----------
435120 2576610
435121 2468963
435122 2250216
435123 2704211