Oracle並行操作——並行DML操作

路途中的人2012發表於2016-09-19

 

對大部分的OLTP系統而言,並行DMLPDML)的應用場景不多。大多數的PDML操作集中在下面幾個場景下:

 

ü        系統移植,從舊系統中匯入原始資料和基礎資料;

ü        資料倉儲系統Data Warehouse定期進行大批次原始資料匯入和清洗;

ü        藉助一些專門的工具,如sql loader,進行資料海量匯入;

 

本篇主要介紹並行DML操作的一些細節和注意方面。

 

1、環境準備

 

Oracle並行操作前提兩個條件,其一是盈餘的軟硬體資源,其二是海量的大資料量操作。

 

//作業系統和DB環境

SQL> select * from v$version where rownum<2;

 

BANNER

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

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

 

SQL> show parameter cpu_count;

 

NAME                                 TYPE                   VALUE

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

cpu_count                            integer                4

SQL>

 

//資料環境

SQL> select count(*) from t;

 

  COUNT(*)

----------

  10039808

 

Executed in 4.072 seconds

 

 

 

2、並行統計量收集

 

為了實現CBO的正常工作,我們通常要保證Oracle資料字典中保留有關於資料表完全的統計資訊描述。統計資訊包括資料行數、取值分佈、離散程度等等指標。收集統計量是一項比較重要的工作。當資料表很大的時候,即使使用了比例抽樣的方法,進行彙總統計的資料量也是很大。所以這種場合下,是可以應用到並行技術的。

 

在目前的Oracle版本中,通常是使用dbms_stats包進行統計量收集。相對於過去的analyze table xxx命令,dbms_stats包對於統計量收集更加完全,應對分割槽狀況更好。在dbms_stats方法中,存在引數degree,表示並行度,可以直接指定希望的收集並行度。

 

 

--收集統計量,指定並行度

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

PL/SQL procedure successfully completed

 

Executed in 15.32 seconds

 

 

系統使用15.32s的時間完成了收集。

 

在收集過程中,我們觀察v$px_sessionv$px_process兩個檢視的狀態。檢查並行伺服程式池的狀況。

 

SQL> select * from v$px_process;

 

SERVER_NAME STATUS           PID SPID            SID    SERIAL#

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

P006        IN USE           100 19070982             35      50729

P001        IN USE            65 13107452            178      35585

P002        IN USE            73 9633888             184      25268

P003        IN USE            85 22478986            223      33339

P000        IN USE            63 18743314            500      16029

P004        IN USE            95 14221380            509      26446

P005        IN USE            99 23068708            510      20895

 

7 rows selected

 

 

系統依據並行度要求,分配了7個程式進行操作。

 

//並行會話資訊

SQL> select * from v$px_session;

 

SADDR   SID    SERIAL#      QCSID  QCSERIAL#      DEGREE REQ_DEGREE

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

070000007D2BA680        500      16029        324      26152           7   7

070000007FE7EC70        178      35585        324      26152           7   7

070000007FE6D5D0        184      25268        324      26152           7   7

070000007FDFC2C0        223      33339        324      26152           7   7

070000007D2A0490        509      26446        324      26152           7   7

070000007D29D620        510      20895        324      26152           7    7

070000007FC94480         35      50729        324      26152           7    7

070000007D12FB00        324      26152        324             

(篇幅原因,有擷取結果……

8 rows selected

 

 

注意,在請求了並行度degree=7的情況下,Oracle根據CPU數量分配了7個並行slave程式進行操作。會話層面,七個slave程式分別對應七個會話資訊進行並行操作。同時,存在一個額外會話(sid=324),充當全域性協調者coordinator的角色。v$px_session中的qcsid欄位含義為“Session serial number of the parallel coordinator”,就是並行操作中扮演協調者角色的程式。

 

 

如果不使用並行收集,只是簡單的序列收集,我們檢視一下效率情況。

 

 

//指定序列

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

PL/SQL procedure successfully completed

 

Executed in 46.816 seconds

 

 

效果清晰可見,從原來的15s多的收集時間,放大為47s左右,幾乎是三倍的損耗。

 

 

結論:對於統計量收集而言,如果作業時間可以避開業務高峰時間視窗,進行並行操作收集統計量還是一個不錯的選擇。

 

 

3、並行insert操作

 

下面進行並行insert操作,我們選擇使用hint來進行並行控制。

 

//開啟PDML的開關

SQL> alter session enable parallel dml;

Session altered

 

Executed in 0.016 seconds

 

使用hint,開啟8個並行度進行insert操作。

 

 

--並行insert

SQL> insert /*+ parallel(t,8) */ into t select * from t;

10039808 rows inserted

 

Executed in 76.238 seconds

 

 

執行過程中,出現的並行操作過程如下。

 

//開啟8個並行度;

SQL> select * from v$px_session;

 

SADDR                   SID    SERIAL#      QCSID  QCSERIAL#

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

070000007FFF52E0        361       3123        324      26152 

070000007FE84950        176      50028        324      26152

070000007FE7EC70        178      35508        324      26152  

070000007FE0AAF0        218       5994        324      26152 

070000007D29D620        510      20829        324      26152 

070000007D2A0490        509      26391        324      26152 

070000007FC94480         35      50615        324      26152  

070000007FFFAFC0        359      32516        324      26152  

070000007D12FB00        324      26152        324            

 

9 rows selected

 

SQL> select * from v$px_process;

SERVER_NAME STATUS           PID SPID                    SID SERIAL#

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

P006        IN USE           100 19005590                         35      50615

P001        IN USE            69 19398710                        176      50028

P002        IN USE            73 9633968                         178      35508

P003        IN USE            85 23068694                        218       5994

P007        IN USE           102 18743298                        359      32516

P000        IN USE            66 14221352                        361       3123

P005        IN USE            99 21233884                        509      26391

P004        IN USE            95 19071188                        510      20829

 

8 rows selected

 

 

此時,我們嘗試抽取出執行計劃。

 

//shared_pool中嘗試獲取到指定的記錄;

SQL> select sql_text, sql_id, version_count from v$sqlarea where sql_text like 'insert /*+ parallel(t,8) */%';

 

SQL_TEXT                        SQL_ID        VERSION_COUNT

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

insert /*+ parallel(t,8) */ into t select * from t   67wymm0jhw3gv             2

 

Executed in 0.234 seconds

 

 

利用sql_id,嘗試抽取出shared_pool中的執行計劃。

 

//抽取出執行計劃,篇幅原因,有刪節……

SQL> select * from table(dbms_xplan.display_cursor('67wymm0jhw3gv',format => 'advanced',cursor_child_no => 1));

PLAN_TABLE_OUTPUT

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

SQL_ID  67wymm0jhw3gv, child number 1

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

insert /*+ parallel(t,8) */ into t select * from t

Plan hash value: 4064487821

 

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

| Id  | Operation             | Name     | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |

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

|   0 | INSERT STATEMENT      |          |       |       |  2718 (100)|          |        |      |            |

|   1 |  PX COORDINATOR       |          |       |       |            |          |        |      |            |

|   2 |   PX SEND QC (RANDOM) | :TQ10000 |  5019K|   469M|  2718   (1)| 00:00:33 |  Q1,00 | P->S | Q

|   3 |    LOAD AS SELECT     |          |       |       |            |          |  Q1,00 | PCWP |            |

|   4 |     PX BLOCK ITERATOR |          |  5019K|   469M|  2718   (1)| 00:00:33 |  Q1,00 | PCWC |            |

|*  5 |      TABLE ACCESS FULL| T        |  5019K|   469M|  2718   (1)| 00:00:33 |  Q1,00 | PCWP |            |

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

Predicate Information (identified by operation id):

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

   5 - access(:Z>=:Z AND :Z<=:Z)

Note

-----

   - automatic DOP: Computed Degree of Parallelism is 8 because of degree limit

已選擇66行。

 

已用時間:  00: 00: 00.40

 

 

如果不使用並行操作,進行如此規模的insert操作,會如何呢?

 

//使用noparallelhint進行並行抑制;

 

SQL> insert /*+ noparallel */ into t select * from t;

10039808 rows inserted

 

Executed in 87.813 seconds

 

 

對應的執行計劃如下:

 

 

SQL> select sql_text, sql_id, version_count from v$sqlarea where sql_text like 'insert /*+ noparallel */%';

 

SQL_TEXT                                SQL_ID VERSION_COUNT

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

insert /*+ noparallel */ into t select * from t    9u0xcrr3bcjs1             1

 

Executed in 0.234 seconds

 

 

 

SQL> select * from table(dbms_xplan.display_cursor('9u0xcrr3bcjs1',format => 'advanced',cursor_child_no => 0));

 

PLAN_TABLE_OUTPUT

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

SQL_ID  9u0xcrr3bcjs1, child number 0

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

insert /*+ noparallel */ into t select * from t

 

Plan hash value: 2153619298

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

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

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

|   0 | INSERT STATEMENT         |        |       |       | 19601 (100)|          |

|   1 |  LOAD TABLE CONVENTIONAL |      |       |       |            |      |

|   2 |   TABLE ACCESS FULL      | T    |  5019K|   469M| 19601   (1)| 00:03:56 |

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

 

 

 

4、結論

 

本篇對PDML進行了簡單的介紹,包括使用方法和並行度設定。由於篇幅原因,只介紹了並行insert和並行統計量的收集。並行updatedelete本質相同,就不加以累述了。

 

最後,並行操作是一種帶有特殊性的操作,絕對不要將其輕易作為經常性無監管下的操作。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29067253/viewspace-2125154/,如需轉載,請註明出處,否則將追究法律責任。

相關文章