Oracle中如何更新一张大表记录(3)

5、方法3:dbms_paralle_execute并行包使用

其他优化手段都用上的时候,并行是可以尝试的方法。并行parallel就是利用多个process同时进行处理,从而提高处理效率的方法。Parallel的使用有一些前提,也有一些不良反应。并行的前提是硬件支持,并行技术本身要消耗很多的资源,相当于是将服务器资源“榨干”来提速。在规划并行策略的时候,首先要看硬件资源是不是支持,单核CPU情况下,也就不需要使用这个技术了。
 
使用并行之后,必然对其他正在运行程序、作业有影响。所以,笔者的经验是:一般应用不要考虑并行的事情,如果发现特定场景存在并行的需要,可以联系DBA或者运维人员确定可控的技术方案。
 
在11.2之前,使用并行稍微复杂一些,很多朋友在使用的时候经常是“有名无实”,看似设置了并行,但是实际还是单进程运行。11.2之后,Oracle提供了新的并行操作接口dbms_parallel_execute,这让并行更加简单。
 
说明:本篇不是专门介绍dbms_parallel_execute接口,只作为介绍。详细内容参见笔者专门介绍这个接口的文章。

dbms_parallel_execute工作采用作业task方式,后台执行。首先是按照特定的原则进行数据分割,将工作数据集合分割为若干chunk。之后启动多个后台job进行工作。在划分工作集合的问题上,Oracle提供了三种方法,rowid、column_value和SQL,分别按照rowid、列值和特定SQL语句进行分割。
 
注意:使用dbms_parallel_execute接口包有一个前提,就是job_queue_process参数必须设置非空。如果为0,则我们的进程执行之后被阻塞挂起。
 
恢复数据环境。

SQL> create table t_targettablespace users as select * from t where 1=0;

Table created

Executed in 0.078 seconds

SQL> insert /*+append*/into t_target select * from t;

9318016 rows inserted

Executed in 64.974 seconds

SQL> commit;

Commit complete

Executed in 0.109 seconds

参数环境。

SQL> show parameter job_queue

NAME                                TYPE        VALUE

------------------------------------ ----------- ------------------------------

job_queue_processes                  integer    1000

在这个任务中,我们选择使用create_chunks_by_rowid方法。这种方法通用型强,执行计划稳定性好。

SQL> set serveroutput on;

SQL> declare

2    vc_sqlvarchar2(1000);

3    n_try number;

4    n_status number;

5  begin

6    --create a task

7    dbms_parallel_execute.create_task(task_name => 'Huge_Update');

9    --By Rowid

10    dbms_parallel_execute.create_chunks_by_rowid(task_name => 'Huge_Update',

11    table_owner => 'SYS',table_name => 'T_TARGET',by_row =>true,chunk_size => 10000);
 
 12 

13    vc_sql := 'update /*+rowid(dda)*/t_target set owner=to_char(length(owner)) where rowid between :start_id and :end_id';
 
 14 

15    dbms_parallel_execute.run_task(task_name => 'Huge_Update',sql_stmt =>vc_sql,language_flag =>dbms_sql.native,parallel_level => 3);
 
 16  --防止失败后重启

17    n_try := 0;

18    n_status := dbms_parallel_execute.task_status('Huge_Update');

19    while (n_try<2 and (n_status != dbms_parallel_execute.FINISHED)) loop

20      n_try := n_try + 1;

21      dbms_parallel_execute.resume_task('Huge_Update');

22      n_status := dbms_parallel_execute.task_status('Huge_Update');

23    end loop;

24 

25    dbms_output.put_line(''||n_try);

26    dbms_parallel_execute.drop_task('Huge_Update');

27  end;

28  /

0

PL/SQL procedure successfully completed

Executed in 1177.106 seconds

在代码中,需要注意start_id和end_id两个绑定变量。这两个范围值是接口固定的。这种方法使用了1177s来完成工作。

在执行过程中,我们也有很多方法来监督执行过程。Oracle提供了两个视图,关于parallel_execute接口的。Dba_parallel_execute_tasks表示了提交的任务,里面我们可以看到状态信息。
 
 

SQL> col task_name for a15;

SQL> select task_name, status from dba_parallel_execute_tasks;

TASK_NAME          STATUS

------------------- -------------------

Huge_Update        PROCESSING

SQL> select task_name, JOB_PREFIX from dba_parallel_execute_tasks;

TASK_NAME            JOB_PREFIX

-------------------- ------------------------------

Huge_Update          TASK$_655

执行中,我们从v$session中可能看到后台的进程会话。

SQL> select sid, serial#, status, PROGRAM, SQL_ID, event from v$session where action like 'TASK$_655%';
 
 

SID    SERIAL# STATUS  PROGRAM                                          SQL_ID        EVENT
 
---------- ---------- -------- ------------------------------------------------ ------------- ----------------------------------------------------------------
 
        35        507 ACTIVE  oracle@bspdev.localdomain (J003)                d7xw8z3nzh5cg db file scattered read
 
        38        119 ACTIVE  oracle@bspdev.localdomain (J001)                d7xw8z3nzh5cg log buffer space
 
        45      6612 ACTIVE  oracle@bspdev.localdomain (J000)                d7xw8z3nzh5cg Data file init write
 
 

另一个视图更有用dba_parallel_execute_chunks,其中包括了所有的chunk对象。Parallel Execute执行的原则就是数据的划分,这个视图中,可以看到哪些chunk已经完成,哪些没有完成。
 
 

SQL> select status, count(*) from dba_parallel_execute_chunks group by status;

STATUS                COUNT(*)

-------------------- ----------

ASSIGNED                      3

UNASSIGNED                2315

PROCESSED                  571

ASSIGNED状态表示多进程状态正在处理,我们设置了三个后台进程执行。UNASSIGNED表示没有完成,正等待处理。PROCESSED表示已经处理完。

这种方法应该是目前找到比较好的方法。缺点就是代码量比较大。优点是处理使用并行,如果物理条件支持,执行效率是很高的。而且,在海量数据表的情况下,这种策略是很值得推荐的。
 
 

6、结论

更新大量的数据,是我们常见的一种问题场景。无论对开发人员,还是运维人员,都有不同的挑战。笔者本篇要强调的是:没有绝对好的策略,都是针对特别的场景和背景,选取最适合的策略。从而更好地完成任务。盲目的执行SQL语句,是一种典型不负责任的行为,需要避免杜绝。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c16f4273f1e00305577eccf06f654ff9.html