oracle 17439,Oracle 11g 使用 dbms_parallel_execute执行并行更新

上篇我们讨论了dbms_parallel_execute的工作方法、使用流程和特点。本篇继续来讨论其他两种划分Chunk方式。说明:对每种划分策略执行过程中,笔者都进行了不同的实验,来说明其工作特点。

4、By Number Col划分Chunk方法

应该说,使用rowid进行数据表划分可以带来很多好处。每个chunk数据获取过程,本质上就是执行一个范围Range操作。对于rowid而言,直接通过范围检索的效率是相当高的。

与Rowid方法对应两种策略都是依据“数据表列范围”进行chunk划分。By Number Col的方法顾名思义,需要我们指定出一个数字类型列名称。Oracle会依据这个列取值进行划分。每个chunk实际上都是通过数字类型检索到的结果集合进行处理。

当然,这个过程必然伴随着我们对于“地势”条件的依赖。每次从上千万条记录中,FTS的检索出一个chunk数据显然是很费力的操作过程。最直接的优化手段就是索引和分区。注意:如果我们没有特殊的条件进行chunk划分辅助,一定要考虑by number col方式是否适合。

SQL> create index idx_t_id on t(object_id);

Index created

Executed in 107.282 seconds

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true,degree => 2);

PL/SQL procedure successfully completed

Executed in 87.453 seconds

修改的脚本如下:

SQL> declare

2    vc_task varchar2(100);

3    vc_sql varchar2(1000);

4    n_try number;

5    n_status number;

6  begin

7    --Define the Task

8    vc_task := 'Task 2: By Number Col';

9    dbms_parallel_execute.create_task(task_name => vc_task);

10

11    --Define the Spilt

12    dbms_parallel_execute.create_chunks_by_number_col(task_name => vc_task,

13                                                      table_owner => 'SYS',

14                                                      table_name => 'T',

15                                                      table_column => 'OBJECT_ID',

16                                                      chunk_size => 1000); --定义chunk

17

18    vc_sql := 'update /*+ ROWID(dda) */t set DATA_OBJECT_ID=object_id+1 where object_id between :start_id and :end_id';

19    --Run the task

20    dbms_parallel_execute.run_task(task_name => vc_task,

21                                  sql_stmt => vc_sql,

22                                  language_flag => dbms_sql.native,

23                                  parallel_level => 1);

24

25    --Controller

26    n_try := 0;

27    n_status := dbms_parallel_execute.task_status(task_name => vc_task);

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

29      dbms_parallel_execute.resume_task(task_name => vc_task);

30      n_status := dbms_parallel_execute.task_status(task_name => vc_task);

31    end loop;

32

33    --Deal with Result

34    dbms_parallel_execute.drop_task(task_name => vc_task);

35  end;

36  /

从执行流程上看,上面脚本和by rowid方式没有显著地差异。最大的区别在于定义chunk时调用的方法,参数包括指定的数据表、列名和chunk size。注意:我们这里定义了chunk size是1000,但是在执行过程中,我们不能保证每个chunk的大小是1000。这个结论我们在后面的阐述实验中可以证明。

执行脚本的速度显著的比by rowid的慢了很多。但是我们也能发现很多技术细节。首先,我们会有一个时期,在chunk视图中没有结果返回。

SQL> select task_name, chunk_type, status from user_parallel_execute_tasks;

TASK_NAME                CHUNK_TYPE  STATUS

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

Task 2: By Number Col    NUMBER_RANGE CHUNKING

Executed in 0.61 seconds

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

STATUS                COUNT(*)

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

在之后,我们才能查看到chunk处理情况。

SQL> select task_name, chunk_type, status from user_parallel_execute_tasks;

TASK_NAME                CHUNK_TYPE  STATUS

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

Task 2: By Number Col    NUMBER_RANGE PROCESSING

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

STATUS                COUNT(*)

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

ASSIGNED                      1

UNASSIGNED                1557

PROCESSED                    13

这个现象说明:对dbms_parallel_execute包处理过程来说,包括两个重要的步骤,Chunk分块步骤和Chunk处理步骤。无论是哪种分块方法,Oracle都是首先依据分割原则,将任务拆分开来,规划在任务视图里面。之后再进行分作业Job的Processing处理过程。

同by rowid方式中的rowid Range信息一样,我们在chunk视图中也是可以看到数字列范围的信息。

SQL> select task_name, status, start_id, end_id, job_name from user_parallel_execute_chunks where rownum<5;

TASK_NAME                STATUS                START_ID    END_ID JOB_NAME

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

Task 2: By Number Col    PROCESSED                25002      26001 TASK$_5_2

Task 2: By Number Col    ASSIGNED                  26002      27001 TASK$_5_1

Task 2: By Number Col    ASSIGNED                  27002      28001 TASK$_5_2

Task 2: By Number Col    UNASSIGNED                28002      29001

注意:我们此处看到的chunk范围是1000,由于数据准备过程,范围1000绝对不意味着每个chunk的大小是1000。所以,我们也就可以推断出,调用方法中的chunk size在number col方式中,是取值范围的大小。

直观的想,Oracle选取这样的策略也是有依据的:Oracle可以直接选取一个最小和最大的数据列值,依次chunk取值范围进行分割。这样做可减少对数据检索的压力。

在执行过程中,我们跟踪了执行会话的SQL语句,从shared pool中抽取出执行计划。

SQL> select * from table(dbms_xplan.display_cursor(sql_id=>'f2z147unc1n3q'));

PLAN_TABLE_OUTPUT

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

SQL_ID  f2z147unc1n3q, child number 0

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

update /*+ ROWID(dda) */t set DATA_OBJECT_ID=object_id+1 where

object_id between :start_id and :end_id

Plan hash value: 538090111

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

| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |

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

|  0 | UPDATE STATEMENT  |          |      |      | 74397 (100)|          |

|  1 |  UPDATE            | T        |      |      |            |          |

|*  2 |  FILTER          |          |      |      |            |          |

|*  3 |    INDEX RANGE SCAN| IDX_T_ID | 48375 |  472K|  197  (1)| 00:00:03 |

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

Predicate Information (identified by operation id):

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

2 - filter(:START_ID<=:END_ID)

PLAN_TABLE_OUTPUT

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

3 - access("OBJECT_ID">=:START_ID AND "OBJECT_ID"<=:END_ID)

匿名块执行完毕。

32

33    --Deal with Result

34    dbms_parallel_execute.drop_task(task_name => vc_task);

35  end;

36  /

PL/SQL procedure successfully completed

Executed in 11350.421 seconds

完成时间大大增加,折合3个小时左右。这个实验告诉我们:在三种方法选取如果不合适,性能会大大降低。

下面我们来看最后一种方法by SQL。

5、by SQL方法进行chunk划分

By SQL方法是用户自己定义SQL语句,获取column的start id和end id作为划分chunk的内容。代码如下:

SQL> declare

2    vc_task varchar2(100);

3    vc_sql varchar2(1000);

4    vc_sql_mt varchar2(1000);

5    n_try number;

6    n_status number;

7  begin

8    --Define the Task

9    vc_task := 'Task 3: By SQL';

10    dbms_parallel_execute.create_task(task_name => vc_task);

11

12    --Define the Spilt

13    vc_sql_mt := 'select distinct object_id, object_id from t';

14    dbms_parallel_execute.create_chunks_by_SQL(task_name => vc_task,

15                                              sql_stmt => vc_sql_mt,

16                                              by_rowid => false);

17

18    vc_sql := 'update /*+ ROWID(dda) */t set DATA_OBJECT_ID=object_id+1 where object_id between :start_id and :end_id';

19    --Run the task

20    dbms_parallel_execute.run_task(task_name => vc_task,

21                                  sql_stmt => vc_sql,

22                                  language_flag => dbms_sql.native,

23                                  parallel_level => 2);

24

25    --Controller

26    n_try := 0;

27    n_status := dbms_parallel_execute.task_status(task_name => vc_task);

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

29      dbms_parallel_execute.resume_task(task_name => vc_task);

30      n_status := dbms_parallel_execute.task_status(task_name => vc_task);

31    end loop;

32

33    --Deal with Result

34    dbms_parallel_execute.drop_task(task_name => vc_task);

35  end;

36  /

在定义chunk的过程中,我们指定出单独的SQL语句来确定start id和end id。这也就让我们不需要定义所谓的chunk size了。

执行过程依然进行chunking和processing过程。相关视图信息如下:

--chunking过程

SQL> select task_name, chunk_type, status from user_parallel_execute_tasks;

TASK_NAME                CHUNK_TYPE  STATUS

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

Task 3: By SQL            NUMBER_RANGE CHUNKING

--Processing过程

SQL> select task_name, chunk_type, status from user_parallel_execute_tasks;

TASK_NAME                CHUNK_TYPE  STATUS

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

Task 3: By SQL            NUMBER_RANGE PROCESSING

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

STATUS                COUNT(*)

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

ASSIGNED                      2

UNASSIGNED                75559

PROCESSED                    25

--执行作业情况

SQL> select saddr, sid, serial#, PROGRAM from v$session where username='SYS' and status='ACTIVE' and oSUSEr='oracle';

SADDR          SID    SERIAL# PROGRAM

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

35ECE400        31        103 oracle@SimpleLinux.localdomain (J000)

35EA8300        45        29 oracle@SimpleLinux.localdomain (J001)

在chunk范围信息中,我们可以印证对于chunk size的理解。

SQL> select chunk_id, task_name, status, start_id, end_id from user_parallel_execute_chunks where rownum<10;

CHUNK_ID TASK_NAME                STATUS                START_ID    END_ID

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

20052 Task 3: By SQL            PROCESSED                17427      17427

20053 Task 3: By SQL            PROCESSED                17439      17439

20054 Task 3: By SQL            PROCESSED                17442      17442

20055 Task 3: By SQL            PROCESSED                17458      17458

20056 Task 3: By SQL            PROCESSED                37321      37321

20057 Task 3: By SQL            PROCESSED                37322      37322

20058 Task 3: By SQL            PROCESSED                17465      17465

20059 Task 3: By SQL            PROCESSED                37323      37323

20060 Task 3: By SQL            PROCESSED                17468      17468

9 rows selected

由于条件的限制,本次执行时间较长。

32

33    --Deal with Result

34    dbms_parallel_execute.drop_task(task_name => vc_task);

35  end;

36  /

PL/SQL procedure successfully completed

Executed in 47522.328 seconds

总执行时间为13个小时。

6、结论

从上面的实验,我们可以了解dbms_parallel_execute新功能包的使用和功能特点。比较显著的就是区别与传统的并行设置,parallel_execute包的方法是依托于10g以来的job schedule机制。并行、多线程转化为多个后台作业自主运行完成。

应该说,这样的策略让并行变的更加简单易用。我们将关注点转移到如何进行chunk划分和设置多少并行度的问题上。Chunk的划分影响到的是每次处理的数据量,而并行度取决于实际系统的资源富裕程度。

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值