並行查詢的執行計劃解讀
in-out欄位的pcwc或是pcwp代表什麼意思?
一、執行計劃裡的(單獨型)操作一般都是子操作在父操作前面執行,表現在並行查詢語句的執行計劃裡的in_out欄位(執行計劃裡當發生並行查詢時才會出現的欄位)就是pcwp,即子操作在父操作前面執行,亦即在一個程序集(同一個程序集的各個程序同時用於執行同一種操作,故而稱該操作為並行操作)同一個程序可以執行完一個操作後再執行其父操作(這裡,子操作和父操作有個執行先後順序)
例如,
EXPLAIN PLAN FOR
SELECT SUM(salary) FROM emp2 GROUP BY department_id;
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) | TQ |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 2782 | 3 (34) | | | |
| 1 | PX COORDINATOR | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10001 | 107 | 2782 | 3 (34) | Q1,01 | P->S | QC (RAND) |
| 3 | HASH GROUP BY | | 107 | 2782 | 3 (34) | Q1,01 | PCWP | |
| 4 | PX RECEIVE | | 107 | 2782 | 3 (34) | Q1,01 | PCWP | |
| 5 | PX SEND HASH | :TQ10000 | 107 | 2782 | 3 (34) | Q1,00 | P->P | HASH |
| 6 | HASH GROUP BY | | 107 | 2782 | 3 (34) | Q1,00 | PCWP | |
| 7 | PX BLOCK ITERATOR | | 107 | 2782 | 2 (0) | Q1,00 | PCWP | |
| 8 | TABLE ACCESS FULL| EMP2 | 107 | 2782 | 2 (0) | Q1,00 | PCWP | |
--------------------------------------------------------------------------------------------------------
從下往上,從右向左讀時,先執行 TABLE ACCESS FULL,故而在其欄位IN-OUT為PCWP,再執行PX BLOCK ITERATOR,故而在其欄位IN-OUT為PCWP,最後,執行 HASH GROUP BY ,故而在其欄位IN-OUT為PCWP。註釋:
0、| 7 | PX BLOCK ITERATOR | | 107 | 2782 | 2 (0) | Q1,00 | PCWP | |
其實,這裡應該為PCWC。該例子是從別人博文上轉載的,估計是有誤的。不過拿來當例子說明正好。
1、in-out欄位的意思是in代表生產者,即作為生產者的程序向表佇列裡放入資料,out代表消費者,即作為消費者的程序向表佇列裡拿出資料。
2、同一個程序集的程序可以對應執行多個操作(操作指的是執行計劃裡顯示的operation欄位裡的操作),比如,先執行操作A,再執行操作B。同一個程序集的各個程序的行為是一樣的,就是說同是程序集P裡的程序E和F,若E的行為是先執行操作A,再執行操作B,則F的行為也是先執行操作A,再執行操作B。即使F的行為的操作和E一樣,但是先後順序不一樣,即先執行操作B,再執行操作A,那也是不一樣的行為。
二、執行計劃裡的(單獨型)操作若是父操作在子操作前面執行,則表現在並行查詢語句的執行計劃裡的in_out欄位(執行計劃裡當發生並行查詢時才會出現的欄位)就是pcwc,即父操作在子操作前面執行,亦即在一個程序集(同一個程序集的各個程序同時用於執行同一種操作,故而稱該操作為並行操作)同一個程序可以執行完一個操作後再執行其子操作(這裡,子操作和父操作有個執行先後順序)。例如,
select /*+ parallel(t1 4) parallel(t2 4) */rownum, t1.idfrom t1, t2 where t1.id = t2.id;
------------------------------------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes |Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 600 | 5(20)| 00:00:01 | | | |
| 1 | COUNT | | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) |:TQ10002 | 100 |600 | 5 (20)| 00:00:01 | Q1,02 | P->S | QC (RAND) |
|* 4 | HASH JOIN BUFFERED | | 100 | 600 | 5(20)| 00:00:01 | Q1,02 | PCWP | |
| 5 | PX RECEIVE | | 100 | 300 | 2(0)|00:00:01 | Q1,02 | PCWP | |
| 6 | PX SEND HASH | :TQ10000 | 100 | 300 |2 (0)| 00:00:01 | Q1,00 | P->P |HASH |
| 7 | PX BLOCK ITERATOR | | 100 | 300 | 2(0)| 00:00:01 | Q1,00 | PCWC | |
| 8 | TABLE ACCESSFULL | T1 | 100 | 300 | 2(0)| 00:00:01 | Q1,00 | PCWP | |
| 9 | PX RECEIVE | | 100 | 300 | 2(0)|00:00:01 | Q1,02 | PCWP | |
| 10 | PX SEND HASH | :TQ10001 | 100 | 300| 2 (0)| 00:00:01 | Q1,01 | P->P | HASH |
| 11 |PX BLOCK ITERATOR | | 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | PCWC | |
| 12 | TABLE ACCESS FULL| T2 | 100 | 300 | 2 (0)|00:00:01 | Q1,01 | PCWP ||
------------------------------------------------------------------------------------------------------------------
從下往上,從右向左讀時,應先執行TABLE ACCESS FULL但是PX BLOCK ITERATOR的in_out欄位裡的值為PCWC,故而先執行PX BLOCK ITERATOR,再執行TABLE ACCESS FULL。執行好TABLE ACCESS FULL後,根據自己的in_out欄位裡的值為PCWP再去執行PX BLOCK ITERATOR,則發現PX BLOCK ITERATOR已經執行過,故而執行上上個操作。
註釋:
1、PX BLOCK ITERATOR:這個操作通常處於並行管道(即並行操作)的第一步,BLOCK ITERATOR把表分隔成多個(區域)塊,每個(區域)塊由涉及的並行伺服器程序(slave process)中的一個去處理。(換句話說,在協調者程序QC預先對作用於同一個表的各個程序分配好各個程序負責的表區域後,各個程序執行BLOCK ITERATOR就是找到並確認標記下協調者程序QC分配給他們的區域。)
一般地,BLOCK ITERATOR 和TABLE ACCESS FULL操作是成對出現的。
前者是父操作,後者是子操作。
前者的in_out欄位裡的值為PCWC,後者的in_out欄位裡的值為PCWP。也就是說,要先分割好區域後,各個程序才能在各自區域進行全表掃描。
2、除了按資料塊分割表區域,即PX BLOCK ITERATOR外,還有一種方法來分割表區域:按表分割槽來分割表區域,即PX PARTION ITERATOR。
3、PX BLOCK ITERATOR操作,是到目前為止本人有見過的其in_out欄位值為PCWC的一種操作。
三、當發生並行查詢時,其執行計劃裡的(單獨型)子操作和父操作間的執行順序還有另一種情況(除了子操作在父操作前面執行和父操作在子操作前面執行這兩種情況外),就是s->p或是p->p、p->s。
註釋:
判斷是否有操作間的並行,看執行計劃裡的in-out欄位是否有出現s->p或是p->p、p->s,出現這些時name欄位裡總是會出現表佇列的(TQ10002,其中1表示dfo_number((來自v$pq_tqstat的欄位)),2表示tq_id(來自v$pq_tqstat的欄位))。
總結:
並行操作間關係(執行計劃中in-out部分,指明瞭操作中資料流的方向)p-s:並行程序集傳送資料給序列程序(例如,協調者程序QC就是單個程序,或說序列程序)。p-p:(有2組slavesprocess時使用),一個並行操作將資料傳送給另一個並行操作。
s-p:序列傳送資料給並行,效率差(1.單一程序產生資料沒有多個程序消費資料快,消費者花很多時間等資料而不是處理資料,2.序列執行操作和並行執行操作傳送資料有一些不必要的通訊)。
PCWP:在一個程序集裡的同一個slave process(即同一個並行伺服器程序)並行執行一個操作及其父操作(操作指的是執行計劃裡顯示的operation欄位裡的操作,不是指的是sql語句的各個子句(這種就是所謂的sql語句級別上的操作),一個子句可以由多個這裡的所謂操作組成。此操作時該操作對應的同一組內的程序間可以有互動,而沒有組間通訊程序集裡的程序間無通訊,即程序在執行該操作時還不到傳送資料給表佇列的時候。PCWC:相同slaveprocess並行執行一個操作及其子操作,無通訊。
================================================================================================================================
說到同一個程序集的單個程序可以對應執行多個操作,那麼在執行計劃裡怎麼可以看出哪些操作是對應於同一個程序(換言之,一個程序會做哪些操作)呢?
同一個程序集的程序可以對應執行多個操作(操作指的是執行計劃裡顯示的operation欄位裡的操作),比如,先執行操作A,再執行操作B。同一個程序集的各個程序的行為是一樣的,就是說同是程序集P裡的程序E和F,若E的行為是先執行操作A,再執行操作B,則F的行為也是先執行操作A,再執行操作B。即使F的行為的操作和E一樣,但是先後順序不一樣,即先執行操作B,再執行操作A,那也是不一樣的行為。
說到同一個程序集的單個程序可以對應執行多個操作,那麼在執行計劃裡怎麼可以看出哪些操作是對應於同一個程序(換言之,一個程序會做哪些操作)呢?我們要從執行計劃裡顯示的(底部的)子操作開始看,看執行計劃裡該操作的in_out欄位的值是pcwc或是pcwp這一類,還是s->p或是p->p、p->s這一類的。屬於後者的話,說明該操作對應一個程序。屬於前者的話,該操作只是對應一個程序裡的一步,還看父操作的in_out字端是pcwc或是pcwp這一類,還是s->p或是p->p、p->s這一類的。屬於後者的話,說明該程序對應兩個操作。屬於前者的話,還看其父操作的in_out字端是屬於哪一類的。以此類推。總之,只要最終找到了哪個操作in_out字端是s->p或是p->p、p->s這一類的,就可以確定同一個程序集的單個程序可以對應執行哪些操作了。
例如,
select/*+ parallel(t1 4) parallel(t2 4) */ rownum, t1.id from t1, t2where t1.id = t2.id;
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 600 | 5 (20)| 00:00:01 | | | |
| 1 | COUNT | | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10002| 100 | 600 | 5 (20)| 00:00:01 | Q1,02 | P->S | QC (RAND) |
|* 4 | HASH JOIN BUFFERED | | 100 | 600 | 5 (20)| 00:00:01 | Q1,02 | PCWP| |
| 5 | PX RECEIVE | | 100 | 300 | 2 (0)| 00:00:01 | Q1,02 | PCWP| |
| 6 | PX SEND HASH | :TQ10000| 100 | 300 | 2 (0)| 00:00:01 | Q1,00 | P->P | HASH |
| 7 | PX BLOCK ITERATOR | | 100 | 300 | 2 (0)| 00:00:01 | Q1,00 | PCWC | |
| 8 | TABLE ACCESS FULL| T1 | 100| 300 | 2 (0)| 00:00:01 | Q1,00 | PCWP| |
| 9 | PX RECEIVE | | 100 | 300 | 2 (0)| 00:00:01 | Q1,02 | PCWP| |
| 10 | PX SEND HASH | :TQ10001| 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | P->P | HASH |
| 11 | PX BLOCK ITERATOR | | 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | PCWC | |
| 12 | TABLE ACCESS FULL| T2 | 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | PCWP | |
------------------------------------------------------------------------------------------------------------------
1、Id從12到10的這些操作對應為我們取名叫A的程序集裡的程序執行的操作。
2、Id從8到6的這些操作對應為我們取名叫B的程序集裡的程序執行的操作。
3、我們再看id為9和id為5的操作,id為9和id為5的操作的in_out欄位的值是屬於pcwc或是pcwp這一類的。id為9和id為5的操作是並列的(因為他們向右縮排一樣的距離),故而他們都是id為4的操作的子操作,id為4的操作就是他們的父操作。id為4的操作的in_out欄位的值是pcwc或是pcwp這一類的。故而我們再看id為4的操作的父操作,即d為3的操作,該操作的in_out欄位的值是屬於s->p或是p->p、p->s這一類的。所以id為9、5、4、3的操作,對應為我們取名叫C的程序集裡的程序執行的操作。
4、 Id為2的操作是PX COORDINATOR,即協調者操作,它是一個序列操作,就是說該操作對應的程序集裡只有一個程序,不是多個。我們取名該程序集叫C。如果一個程序集裡有多個程序,那叫並行操作(的程序)。
圖形化說明如下:
我們從下文的DFO結構圖(由v$pq_tqstat檢視畫出)看出,程序集A和程序集B其實是同一個程序集,即程序集(P4、P5、P6、P7)。也就是說對錶T1和表T2的全表掃描等操作是由同一個程序集完成的。補充:
1、凡是in_out欄位的值是屬於s->p或是p->p、p->s這一類的操作都是px send 操作。凡是in_out欄位的值是屬於s->p或是p->p、p->s這一類的操作對應的name欄位都有一個值,且該值為一個表佇列TQ。
2、TQ欄位裡有值的操作,表明該操作有生產者的角色(不排除同時兼有消費者角色對另一個表佇列),即該操作將資料放到TQ欄位裡的表佇列上。
我們從執行計劃裡,可以看出在並行化執行SQL語句時整個DFO結構圖裡哪些操作是對應於同一個程序。但是我們此時還不知道該程序是誰,該程序所屬的程序集還有哪些程序。檢視v$pq_tqstat就可以解決這個問題。
我們看上面例子的v$pq_tqstat:
[email protected]> select * from v$pq_tqstat order by dfo_number , tq_id , server_type;
DFO_NUMBER TQ_ID SERVER_TYPE NUM_ROWS BYTES OPEN_TIME AVG_LATENCY WAITS TIMEOUTS PROCESS INSTANCE
---------- ---------- ------------------------- ---------- ---------- ----------- ---------- ---------- --------------------
1 0 Consumer 28 192 0 0 10 1 P002 1
1 0 Consumer 26 184 0 0 9 1 P001 1
1 0 Consumer 19 156 0 0 10 2 P000 1
1 0 Consumer 27 188 0 0 10 3 P003 1
1 0 Producer 0 80 0 0 0 0 P007 1
1 0 Producer 0 80 0 0 0 0 P005 1
1 0 Producer 100 480 0 0 2 1 P004 1
1 0 Producer 0 80 0 0 0 0 P006 1
1 1 Consumer 28 192 0 0 10 1 P002 1
1 1 Consumer 26 184 0 0 9 1 P001 1
1 1 Consumer 19 156 0 0 10 3 P000 1
1 1 Consumer 27 188 0 0 10 2 P003 1
1 1 Producer 100 480 0 0 1 0 P004 1
1 1 Producer 0 80 0 0 0 0 P007 1
1 1 Producer 0 80 0 0 0 0 P005 1
1 1 Producer 0 80 0 0 0 0 P006 1
1 2 Consumer 100 480 0 0 0 0 QC 1
1 2 Producer 27 128 0 0 0 0 P003 1
1 2 Producer 26 124 0 0 0 0 P001 1
1 2 Producer 28 132 0 0 0 0 P002 1
1 2 Producer 19 96 0 0 0 0 P000 1
欄位dfo_number和tq_id來標識一個表佇列TQ。
從上面的結果看,我們得出存在三個表佇列TQ,分別TQ10、TQ11、TQ12。再看每個表佇列,如表佇列TQ10(即dfo_number=1和tq_id=0的那些資料行),我們根據欄位SERVER_TYPE為Producer和Consumer,可以將PROCESS欄位的程序分成兩個程序集。由此,可以畫出DFO結構圖,如下:
從上面的結果看,有一個DFOtree,三個TQ(table queue),並且p000,p001,p002不僅僅是消費者程序同時也是生產者程序。
同時,我們看到由於是兩個表的連線操作(SQL語句級別的操作),所以TQ(1,0)和TQ(1,1)的生產者的程序集是一樣的,即(P4、P5、P6、P7)。
若兩個表的連線操作時,兩個表的並行度不一樣,則是以大的並行度為準。如,select/*+parallel(t1 2) parallel(t2 4) */rownum, t1.id from t1, t2where t1.id = t2.id;,則存在九個程序(1+2*4)。
DFO結構圖和下圖是對應的:
註釋:
1、DFO即“Data FlowOperator”(資料流操作過程,即從全表掃描獲得的表的資料開始,對這些資料進行同時哪幾種操作,即操作間的並行化。當然,操作內部也是並行化的)。
2、執行計劃裡的name欄位裡出現的表佇列,如TQ10002,其中1表示dfo_number(來自v$pq_tqstat的欄位),2表示tq_id(來自v$pq_tqstat的欄位)。
================================================================================================================================
一般,一個SQL語句有存在幾個子查詢,則並行執行時就有幾個QC(協調者程序),也即存在幾個DFotree。
一個DFO tree,對應一個沒有有子查詢的SQL語句。一個包括含有子查詢的SQL語句,則一個SQL語句有存在幾個子查詢,則並行執行時就有幾個QC(協調者程序),也即存在幾個DFotree。在一個DFO
tree(對應一個沒有有子查詢的SQL語句)裡,並行執行的操作包含程序個數都是一樣的,等於並行度。
例如,
select /*+ parallel(t1 4) parallel(v1 8 ) */ t1.id from t1, (select /*+ parallel(t1 4) parallel(t2 4) */ rownum, t1.id from t1, t2 where t1.id = t2.id) v1 where t1.id = v1.id;它的執行計劃:
------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 600 | 7 (15)| 00:00:01 | | | | | 1 | PX COORDINATOR | | | | | | | | | | 2 | PX SEND QC (RANDOM) | :TQ20002 | 100 | 600 | 7 (15)| 00:00:01 | Q2,02 | P->S | QC (RAND) | |* 3 | HASH JOIN | | 100 | 600 | 7 (15)| 00:00:01 | Q2,02 | PCWP | | | 4 | PX RECEIVE | | 100 | 300 | 2 (0)| 00:00:01 | Q2,02 | PCWP | | | 5 | PX SEND HASH | :TQ20001 | 100 | 300 | 2 (0)| 00:00:01 | Q2,01 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | 100 | 300 | 2 (0)| 00:00:01 | Q2,01 | PCWC | | | 7 | TABLE ACCESS FULL | T1 | 100 | 300 | 2 (0)| 00:00:01 | Q2,01 | PCWP | | | 8 | BUFFER SORT | | | | | | Q2,02 | PCWC | | | 9 | PX RECEIVE | | 100 | 300 | 5 (20)| 00:00:01 | Q2,02 | PCWP | | | 10 | PX SEND HASH | :TQ20000 | 100 | 300 | 5 (20)| 00:00:01 | | S->P | HASH | | 11 | VIEW | | 100 | 300 | 5 (20)| 00:00:01 | | | | | 12 | COUNT | | | | | | | | | | 13 | PX COORDINATOR | | | | | | | | | | 14 | PX SEND QC (RANDOM) | :TQ10002 | 100 | 600 | 5 (20)| 00:00:01 | Q1,02 | P->S | QC (RAND) | |* 15 | HASH JOIN BUFFERED | | 100 | 600 | 5 (20)| 00:00:01 | Q1,02 | PCWP | | | 16 | PX RECEIVE | | 100 | 300 | 2 (0)| 00:00:01 | Q1,02 | PCWP | | | 17 | PX SEND HASH | :TQ10000 | 100 | 300 | 2 (0)| 00:00:01 | Q1,00 | P->P | HASH | | 18 | PX BLOCK ITERATOR | | 100 | 300 | 2 (0)| 00:00:01 | Q1,00 | PCWC | | | 19 | TABLE ACCESS FULL| T1 | 100 | 300 | 2 (0)| 00:00:01 | Q1,00 | PCWP | | | 20 | PX RECEIVE | | 100 | 300 | 2 (0)| 00:00:01 | Q1,02 | PCWP | | | 21 | PX SEND HASH | :TQ10001 | 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | P->P | HASH | | 22 | PX BLOCK ITERATOR | | 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | PCWC | | | 23 | TABLE ACCESS FULL| T2 | 100 | 300 | 2 (0)| 00:00:01 | Q1,01 | PCWP | | -------------------------------------------------------------------------------------------------------------------------
Information from v$pq_tqstat:
[email protected]> select * from v$pq_tqstat order by dfo_number , tq_id , server_type; DFO_NUMBER TQ_ID SERVER_TYPE NUM_ROWS BYTES OPEN_TIME AVG_LATENCY WAITS TIMEOUTS PROCESS INSTANCE ---------- ---------- --------------- ---------- ---------- ---------- ----------- ---------- ---------- ---------- ---------- 1 0 Consumer 27 128 0 0 14 8 P003 1 1 0 Consumer 19 96 0 0 12 6 P000 1 1 0 Consumer 26 124 0 0 12 6 P001 1 1 0 Consumer 28 132 0 0 14 8 P002 1 1 1 Consumer 27 188 0 0 13 7 P003 1 1 1 Consumer 26 184 0 0 14 9 P001 1 1 1 Consumer 28 192 0 0 11 6 P002 1 1 1 Consumer 19 156 0 0 14 9 P000 1 1 1 Producer 0 80 0 0 0 0 P006 1 1 1 Producer 100 480 0 0 1 0 P004 1 1 1 Producer 0 80 0 0 0 0 P005 1 1 1 Producer 0 80 0 0 0 0 P007 1 1 2 Producer 26 124 0 0 0 0 P001 1 1 2 Producer 19 96 0 0 0 0 P000 1 1 2 Producer 28 132 0 0 0 0 P002 1 1 2 Producer 27 128 0 0 0 0 P003 1 2 0 Consumer 26 184 0 0 14 7 P009 1 2 0 Consumer 19 156 0 0 15 8 P008 1 2 0 Consumer 28 192 0 0 13 7 P010 1 2 0 Consumer 27 188 0 0 13 8 P011 1 2 0 Producer 100 480 0 0 2 1 P016 1 2 0 Producer 0 80 0 0 0 0 P013 1 2 0 Producer 100 480 0 0 4294967242 4294967282 QC 1 2 0 Producer 0 80 0 0 0 0 P014 1 2 0 Producer 0 80 0 0 0 0 P012 1 2 1 Consumer 19 156 0 0 14 8 P008 1 2 1 Consumer 27 188 0 0 14 8 P011 1 2 1 Consumer 28 192 0 0 16 7 P010 1 2 1 Consumer 26 184 0 0 13 7 P009 1 2 1 Producer 100 480 0 0 1 0 P016 1 2 1 Producer 0 80 0 0 0 0 P014 1 2 1 Producer 0 80 0 0 0 0 P012 1 2 1 Producer 0 80 0 0 0 0 P013 1 2 2 Consumer 100 480 0 0 2 1 QC 1 2 2 Consumer