溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么理解Oracle的并行執(zhí)行

發(fā)布時(shí)間:2021-11-10 10:25:52 來(lái)源:億速云 閱讀:151 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫(kù)

這篇文章主要介紹“怎么理解Oracle的并行執(zhí)行”,在日常操作中,相信很多人在怎么理解Oracle的并行執(zhí)行問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么理解Oracle的并行執(zhí)行”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

術(shù)語(yǔ)說(shuō)明:

  1. S: 時(shí)間單位秒。

  2. K: 數(shù)量單位一千。

  3. M: 數(shù)量單位一百萬(wàn), 或者時(shí)間單位分鐘。

  4. DoP: Degree of Parallelism, 并行執(zhí)行的并行度。

  5. QC: 并行查詢的 Query Coordinator。

  6. PX 進(jìn)程: Parallel Execution Slaves。

  7. AAS: Average active session, 并行執(zhí)行時(shí)平均的活動(dòng)會(huì)話數(shù)。

  8. 分發(fā): pq distribution method, 并行執(zhí)行的分發(fā)方式, 包括 replicate, broadcast, hash 和 adaptive分發(fā)等 4 種方式, 其中 adaptive 分發(fā)是 12c 引入的的新特性, 我將在本篇文章中一一闡述。

  9. Hash join 的左邊: 驅(qū)動(dòng)表, the build side of hash join, 一般為小表。

  10. Hash join 的右邊: 被驅(qū)動(dòng)表, the probe side of hash join, 一般為大表。

  11. 布隆過(guò)濾: bloom filter, 一種內(nèi)存數(shù)據(jù)結(jié)構(gòu), 用于判斷一個(gè)元素是否屬于一個(gè)集合。

測(cè)試環(huán)境和數(shù)據(jù)

Oracle版本為12.1.0.2.2,兩個(gè)節(jié)點(diǎn)的RAC,硬件為ExadataX3--‐8。

這是一個(gè)典型的星型模型,事實(shí)表lineorder有3億行記錄,維度表part/customer分別包含1.2M

和1.5M行記錄,3個(gè)表都沒(méi)有進(jìn)行分區(qū),lineorder大小接近30GB。


select owner seg_owner, segment_name seg_segment_name, round(bytes/1048576,2) SEG_MB from   dba_segments where owner = 'SID' and segment_name in ('LINEORDER','PART','CUSTOMER') / OWNER SEGMENT_NAME SEGMENT_TYPE SEG_MB ------ ------------ ------------ -------- SID LINEORDER TABLE 30407.75 SID CUSTOMER TABLE 168 SID PART TABLE 120

本篇文章所有的測(cè)試,除非特別的說(shuō)明,我關(guān)閉了12c的adaptive plan特性,參數(shù)optimizer_adaptive_features被默認(rèn)設(shè)置為false。Adaptive相關(guān)的特性如cardinality feedback,adaptive distribution method,adaptive join都不會(huì)啟用。如果檢查執(zhí)行計(jì)劃的outline數(shù)據(jù),你會(huì)發(fā)現(xiàn)7個(gè)優(yōu)化器相關(guān)的隱含參數(shù)被設(shè)置為關(guān)閉狀態(tài)。事實(shí)上,12c優(yōu)化器因?yàn)橐隺daptive plan特性,比以往版本復(fù)雜得多,剖析12c的優(yōu)化器的各種新特性,我覺(jué)得非常具有挑戰(zhàn)性,或許我會(huì)在另一篇文章里嘗試一下。

select * from table(dbms_xplan.display_cursor('77457qc9a324k',0,’outline’)); ... Outline Data ------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('12.1.0.2') DB_VERSION('12.1.0.2') OPT_PARAM('_optimizer_use_feedback' 'false') OPT_PARAM('_px_adaptive_dist_method' 'off') OPT_PARAM('_optimizer_dsdir_usage_control' 0) OPT_PARAM('_optimizer_adaptive_plans' 'false') OPT_PARAM('_optimizer_strans_adaptive_pruning' 'false') OPT_PARAM('_optimizer_gather_feedback' 'false') OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false') OPT_PARAM('optimizer_dynamic_sampling' 11) ALL_ROWS …… END_OUTLINE_DATA */

并行初體驗(yàn)

串行執(zhí)行

以下sql對(duì)customers和lineorder連接之后,計(jì)算所有訂單的全部利潤(rùn)。 串行執(zhí)行時(shí)不使用parallel hint:

select /*+ monitor */ sum(lo_revenue) from lineorder, customer where lo_custkey = c_custkey;

串行執(zhí)行時(shí),sql執(zhí)行時(shí)間為1.5分鐘,dbtime為1.5分鐘。執(zhí)行計(jì)劃有5行,一個(gè)用戶進(jìn)程工作完成了對(duì)customer,lineorder兩個(gè)表的掃描,hashjoin,聚合以及返回?cái)?shù)據(jù)的所有操作。此時(shí)AAS(average active sessions)為1,sql執(zhí)行時(shí)間等于db time。幾乎所有的dbtime都為db cpu,72%的cpu花在了第二行的hash join操作。因?yàn)闇y(cè)試機(jī)器為一臺(tái)Exadata X3——8,30GB的IO請(qǐng)求在一秒之內(nèi)處理完成。Celloffload Efficiency等于87%意味著經(jīng)過(guò)存儲(chǔ)節(jié)點(diǎn)掃描,過(guò)濾不需要的列,最終返回計(jì)算節(jié)點(diǎn)的數(shù)據(jù)大小只有30GB的13%。


并行執(zhí)行

使用hint parallel(4),指定DoP=4并行執(zhí)行同樣的sql:

select /*+ monitor parallel(4)*/ sum(lo_revenue) from lineorder, customer where lo_custkey = c_custkey;

SQL執(zhí)行時(shí)間為21s,db time為1.4分鐘。DoP=4,在兩個(gè)實(shí)例上執(zhí)行。執(zhí)行計(jì)劃從5行增加為9行,從下往上分別多了’PXBLOCKITERATOR’, ‘SORTAGGREGATE’, ‘PXSENDQC(RANDOM)’ 和 ’PXCOORDINATOR’ 這四個(gè)操作。

其中3到8行的操作為并行處理,sql的執(zhí)行順序?yàn)椋好總€(gè)PX進(jìn)程掃描維度表customer(第6行),以數(shù)據(jù)塊地址區(qū)間作為單位(第7行)掃描四分之一的事實(shí)表lineorder(第8行),接著進(jìn)行hash join(第5行),然后對(duì)連接之后的數(shù)據(jù)做預(yù)先聚合(第4行),最后把結(jié)果給QC(第三行)。QC接收數(shù)據(jù)(第2行)之后,做進(jìn)一步的匯總(第1行),最后返回?cái)?shù)據(jù)(第0行)。

SQL執(zhí)行時(shí)間比原來(lái)快了4倍,因?yàn)樽钕臅r(shí)間的操作,比如對(duì)lineorder的全表掃描,hashjoin和聚合,我們使用4個(gè)進(jìn)程并行處理,因此最終sql執(zhí)行時(shí)間為串行執(zhí)行的1/4。另一方面,dbtime并沒(méi)有明顯下降,并行時(shí)1.4m,串行時(shí)為1.5m,從系統(tǒng)的角度看,兩次執(zhí)行消耗的系統(tǒng)資源是一樣的。


DoP=4時(shí),因?yàn)闆](méi)有涉及數(shù)據(jù)的分發(fā)(distribution),QC只需分配一組PX進(jìn)程,四個(gè)PX進(jìn)程分別為實(shí)例1和2的p000/p0001。我們可以從系統(tǒng)上查看這4個(gè)PX進(jìn)程。每個(gè)PX進(jìn)程消耗大致一樣的db time,CPU和IO資源。AAS=4,這是最理想的情況,每個(gè)PX進(jìn)程完成同樣的工作量,一直保持活躍。沒(méi)有串行點(diǎn),沒(méi)有并行執(zhí)行傾斜。

AAS=4,查看活動(dòng)信息時(shí),為了更好的展示活動(dòng)信息,注意點(diǎn)掉”CPU Cores”這個(gè)復(fù)選框。


在Linux系統(tǒng)上顯示這四個(gè)PX進(jìn)程。

[oracle@exa01db01 sidney]$ ps -ef | egrep "p00[01]_SSB" oracle 20888 1 4 2014 ? 18:50:59 ora_p000_SSB1 oracle 20892 1 4 2014 ? 19:01:29 ora_p001_SSB1 [oracle@exa01db01 sidney]$ ssh exa01db02 'ps -ef | egrep "p00[01]_SSB"' oracle 56910 1 4 2014 ? 19:01:03 ora_p000_SSB2 oracle 56912 1 4 2014 ? 18:53:30 ora_p001_SSB2

小結(jié)

本節(jié)的例子中,DoP=4,并行執(zhí)行時(shí)分配了4個(gè)PX進(jìn)程,帶來(lái)4倍的性能提升。SQL monitor報(bào)告包含了并行執(zhí)行的總體信息和各種細(xì)節(jié),比如QC,DoP,并行執(zhí)行所在的實(shí)例,每個(gè)PX進(jìn)程消耗的資源,以及執(zhí)行SQL時(shí)AAS。


生產(chǎn)者-消費(fèi)者模型

在上面并行執(zhí)行的例子中,每個(gè)px進(jìn)程都會(huì)掃描一遍維度表customer,然后掃描事實(shí)表lineorder進(jìn)行hash join。這時(shí)沒(méi)有數(shù)據(jù)需要進(jìn)行分發(fā),只需要分配一組px進(jìn)程。這種replicate維度表的行為,是12c的新特性,由參數(shù)_px_replication_enabled控制。

更常見(jiàn)情況是并行執(zhí)行時(shí),QC需要分配兩組PX進(jìn)程,互為生產(chǎn)者和消費(fèi)者協(xié)同工作,完成并行執(zhí)行計(jì)劃。架構(gòu)圖1如下:


Broadcast分發(fā),一次數(shù)據(jù)分發(fā)

為了舉例說(shuō)明兩組px進(jìn)程如何協(xié)作的,設(shè)置_px_replication_enabled為false。QC會(huì)分配兩組PX進(jìn)程,一組為生產(chǎn)者,一組為消費(fèi)者。

見(jiàn)下圖,此時(shí)sql執(zhí)行時(shí)間為23s,執(zhí)行時(shí)間變慢了2s,dbtime仍為1.5分鐘。


最大的變化來(lái)自執(zhí)行計(jì)劃,現(xiàn)在執(zhí)行計(jì)劃有12行。增加了對(duì)customer的并行掃描 PXBLOCKITERATOR (第8行),分發(fā)’PXSENDBROADCAST’和接收’PXRECEIVE’。執(zhí)行計(jì)劃中出現(xiàn)了兩組PX進(jìn)程,除了之前藍(lán)色的多人標(biāo)志,現(xiàn)在出現(xiàn)了紅色的多人標(biāo)志。此時(shí),SQL的執(zhí)行順序?yàn)椋?br/>

  1. 4個(gè)紅色的PX進(jìn)程扮演生產(chǎn)者角色,掃描維度表customer,把數(shù)據(jù)通過(guò)broadcast的方式分發(fā)給每一個(gè)扮演消費(fèi)者的藍(lán)色PX進(jìn)程。因?yàn)镈oP=4,每一條被掃描出來(lái)的記錄被復(fù)制了4份,從sqlmonitor的第9行,customer全表掃描返回1。5m行數(shù)據(jù),第8行的分發(fā)和第7行的接受之時(shí),變成了6m行記錄,每個(gè)作為消費(fèi)者的藍(lán)色px進(jìn)程都持有了一份完整包含所有custome記錄的數(shù)據(jù),并準(zhǔn)備好第5行hashjoin的buildtable。

  2. 4個(gè)作為消費(fèi)者的藍(lán)色PX進(jìn)程,以數(shù)據(jù)塊地址區(qū)間為單位掃描事實(shí)表lineorder(第10/11行);同時(shí)和已經(jīng)持有的customer表的數(shù)據(jù)進(jìn)hashjoin(第5行),然后對(duì)滿足join條件的數(shù)據(jù)做預(yù)聚合(第4行),因?yàn)槲覀儾樵兊哪繕?biāo)是對(duì)所有l(wèi)o_revenue求和,聚合之后每個(gè)PX進(jìn)程只需輸出一個(gè)總數(shù)。

  3. 4個(gè)藍(lán)色的PX進(jìn)程反過(guò)來(lái)作為生產(chǎn)者,把聚合的數(shù)據(jù)發(fā)給消費(fèi)者QC(第3行和第2行)。由QC對(duì)接收到4行記錄做最后的聚合,然后返回給用戶。

  4. 使用broadcast的分發(fā)方式,只需要把customer的數(shù)據(jù)廣播給每個(gè)消費(fèi)者。Lineorder的數(shù)不需要重新分發(fā)。因?yàn)閘ineorder的數(shù)據(jù)量比customer大的多,應(yīng)該避免對(duì)lineorder的數(shù)據(jù)進(jìn)行分發(fā),這種執(zhí)行計(jì)劃非常適合星型模型的數(shù)據(jù)。


觀察sql monitor報(bào)告中Parallel標(biāo)簽下的信息,紅色的PX進(jìn)程為實(shí)例1、2上的p002/p003進(jìn)程,藍(lán)色的PX進(jìn)程為p000/p001進(jìn)程,因?yàn)樗{(lán)色的PX進(jìn)程負(fù)責(zé)掃描事實(shí)表lineorder,hash join和聚合,所以消耗幾乎所有的db time。


生產(chǎn)者-消費(fèi)者模型工作原理

并行查詢之后,可以通過(guò)視圖V$PQ_TQSTAT,驗(yàn)證以上描述的執(zhí)行過(guò)程。

  1. 實(shí)例1、2上的p002/p003進(jìn)程作為生產(chǎn)者,幾乎平均掃描customer的1/4記錄,把每一條記錄廣播給4個(gè)消費(fèi)者PX進(jìn)程,發(fā)送的記錄數(shù)之和為6m行。通過(guò)table queue0(TQ_ID=0),每個(gè)作為消費(fèi)者的p000/p001進(jìn)程,接收了完整的1。5m行customer記錄,接收的記錄數(shù)之和為6m行。

  2. 實(shí)例1、2上的p000/p0001進(jìn)程作為生產(chǎn)者,通過(guò)table queue1(TQ_ID=1),把聚合的一條結(jié)果記錄發(fā)給作為消費(fèi)者的QC。QC作為消費(fèi)者,接收了4行記錄。

SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- -------------- ---------- --------- ---------- 1 0 Producer 1 P002 1461932 1 0 Producer 1 P003 1501892 1 0 Producer 2 P002 1575712 1 0 Producer 2 P003 1460464 1 0 Consumer 1 P000 1500000 1 0 Consumer 1 P001 1500000 1 0 Consumer 2 P000 1500000 1 0 Consumer 2 P001 1500000 1 1 Producer 1 P000 1 1 1 Producer 1 P001 1 1 1 Producer 2 P000 1 1 1 Producer 2 P001 1 1 1 Consumer 1 QC 4 13 rows selected.

那么,以上的輸出中,DFO_NUMBER和TQ_ID這兩列表示什么意思呢?

  1. DFO代表Data Flow Operator,是執(zhí)行計(jì)劃中可以并行執(zhí)行的操作。一個(gè)QC代表一棵DFO樹(shù)(tree),包含多個(gè)DFO;同一個(gè)QC中所有并行操作的DFO_NUMBER是相同的,此例中,所有DFO_NUMBER為1。執(zhí)行計(jì)劃包含多個(gè)QC的例子也不少見(jiàn),比如使用unionall的語(yǔ)句,unionall每個(gè)分支都是獨(dú)立的DFO樹(shù),不同的DFO樹(shù)之間可以并行執(zhí)行。本篇文章僅討論執(zhí)行計(jì)劃只有一個(gè)QC的情況。

  2. TQ代表table queue,用以PX進(jìn)程之間或者和QC通信連接。以上執(zhí)行計(jì)劃中,table queue0為PX進(jìn)程之間的連接,table queue1為PX進(jìn)程和QC之間的連接。生產(chǎn)者通過(guò)table queue分發(fā)數(shù)據(jù),消費(fèi)者從tablequeue接收數(shù)據(jù)。不同的table queue編號(hào),代表了不同的數(shù)據(jù)分發(fā)。通過(guò)table queue,我們可以理解Oracle并行執(zhí)行使用生產(chǎn)者--‐消費(fèi)者模型的本質(zhì):

  • 同一棵DFO樹(shù)中,最多只有兩組PX進(jìn)程。每個(gè)生產(chǎn)者進(jìn)程都存在一個(gè)和每個(gè)消費(fèi)者進(jìn)程的連接,每個(gè)PX進(jìn)程和QC都存在一個(gè)連接。假設(shè)DoP=n,連接總數(shù)為(n*n+2*n),隨著n的增長(zhǎng),連接總數(shù)會(huì)爆炸型增長(zhǎng)。Oracle并行執(zhí)行設(shè)計(jì)時(shí),采用生產(chǎn)者和消費(fèi)者模型,考慮到連接數(shù)的復(fù)雜度,每個(gè)DFO最多只分配兩組PX進(jìn)程。假設(shè)DoP=100時(shí),兩組PX進(jìn)程之間的連接總數(shù)為10000。假設(shè)可以分配三組PX進(jìn)程一起完成并行執(zhí)行計(jì)劃,那么三組PX之間連接總數(shù)會(huì)等于1百萬(wàn),維護(hù)這么多連接,是一個(gè)不可能的任務(wù)。

  • 同一棵DFO樹(shù)中,兩組PX進(jìn)程之間,同一時(shí)間只存在一個(gè)活躍的數(shù)據(jù)分發(fā)。如果執(zhí)行路徑很長(zhǎng),數(shù)據(jù)需要多次分發(fā),兩組PX進(jìn)程會(huì)變換生產(chǎn)者消費(fèi)者角色,相互協(xié)作,完成所有并行操作。每次數(shù)據(jù)分發(fā),對(duì)應(yīng)的tablequeue的編號(hào)不同。一個(gè)活躍的數(shù)據(jù)分發(fā)過(guò)程,需要兩組PX進(jìn)程都參與,一組為生產(chǎn)者發(fā)送數(shù)據(jù),一組為消費(fèi)者接收數(shù)據(jù)。因?yàn)橐粋€(gè)DFO里最多只有兩組PX進(jìn)程,意味著,PX進(jìn)程之間,同一時(shí)間只能有一個(gè)活躍的數(shù)據(jù)分發(fā)。如果PX進(jìn)程在執(zhí)行計(jì)劃中需要多次分發(fā)數(shù)據(jù),可能需要在執(zhí)行計(jì)劃插入一些阻塞點(diǎn),比如BUFFERSORT和HASHJOINBUFFERED這兩個(gè)操作,保證上一次的數(shù)據(jù)分發(fā)完成之后,才開(kāi)始下一次分發(fā)。在后面的章節(jié),我將會(huì)說(shuō)明這些阻塞點(diǎn)帶來(lái)什么影響。這個(gè)例子中,tablequeue0和1可以同時(shí)工作是因?yàn)椋簍ablequeue0是兩組PX進(jìn)程之間的鏈接,tablequeue1為PX進(jìn)程和QC之間的連接,tablequeue0與tablequeue1是相互獨(dú)立的,因此可以同時(shí)進(jìn)行。

  • PX進(jìn)程之間或者與QC的連接至少存在一個(gè)(單節(jié)點(diǎn)下至多三個(gè),RAC環(huán)境下至多四個(gè))消息緩沖區(qū)用于進(jìn)程間數(shù)據(jù)交互,該消息緩沖區(qū)默認(rèn)在Largepool中分配(如果沒(méi)有配置Largepool則在Sharedpool中分配)。多個(gè)緩沖區(qū)是為了實(shí)現(xiàn)異步通信,提高性能。

  • 每個(gè)消息緩沖區(qū)的大小由參數(shù)parallel_execution_message_size控制,默認(rèn)為16k。

  • 當(dāng)兩個(gè)進(jìn)程都在同一個(gè)節(jié)點(diǎn)的時(shí)候,通過(guò)在Largepool(如果沒(méi)有配置Largepool則Sharedpool)中傳遞和接收消息緩沖進(jìn)行數(shù)據(jù)交互。當(dāng)兩個(gè)進(jìn)程位于不同節(jié)點(diǎn)時(shí)。通過(guò)RAC心跳網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)交互,其中一方接收的數(shù)據(jù)需要緩存在本地Largepool(如果沒(méi)有配置Largepool則Sharedpool)里面。

小結(jié)

為了說(shuō)明并行執(zhí)行的生產(chǎn)者--消費(fèi)者模型是如何工作的,我使用了broad cast分發(fā),QC分配兩組PX進(jìn)程,一組為生產(chǎn)者,一組為消費(fèi)者。QC和PX進(jìn)程之間,兩組PX進(jìn)程之間通過(guò)table queue進(jìn)行數(shù)據(jù)分發(fā),協(xié)同完成整個(gè)并行執(zhí)行計(jì)劃。視圖V$PQ_TQSTAT記錄了并行執(zhí)行過(guò)程中,數(shù)據(jù)是如何分發(fā)的。通過(guò)對(duì)DFO,table queue的描述,我闡述生產(chǎn)者--‐消費(fèi)者模型的工作原理和通信過(guò)程,或許有些描述對(duì)你來(lái)說(shuō)過(guò)于突然,不用擔(dān)心,后面的章節(jié)我會(huì)通過(guò)更多的例子來(lái)輔助理解。


如何閱讀并行執(zhí)行計(jì)劃

Table queue 的編號(hào)代表了并行執(zhí)行計(jì)劃中,數(shù)據(jù)分發(fā)的順序。理解執(zhí)行計(jì)劃中的并行操作是如何被執(zhí)行的,原則很簡(jiǎn)單:跟隨Tablequeue的順序。

通過(guò)sqlmonitor報(bào)告判斷sql的執(zhí)行順序,需要結(jié)合name列的tablequeue名字比如:TQ10000(代表DFO=1,tablequeue0),:TQ10001(代表DFO=1,tablequeue1),還有PX進(jìn)程的顏色,進(jìn)行確定。

下面的例子為dbms_xplan。display_cursor 的輸出。對(duì)于并行執(zhí)行計(jì)劃,會(huì)多出來(lái)三列:

1. TQ列:為Q1:00或者Q1:01,其中Q1代表第一個(gè)DFO,00或者01代表tablequeue的編號(hào)。

a. ID7~9的操作的TQ列為Q1,00,該組PX進(jìn)程,作為生產(chǎn)者首先執(zhí)行,然后通過(guò)broadcast 的分發(fā)方式,把數(shù)據(jù)發(fā)給消費(fèi)者。

b. ID10~11,3~6的操作的TQ列為Q1,01,該組PX進(jìn)程作為消費(fèi)者接受customer的數(shù)據(jù)之后,掃描lineorder,hashjoin,聚合之后,又作為生產(chǎn)者通過(guò)tablequeue2把數(shù)據(jù)發(fā)給QC。

2. In--‐out 列:表明數(shù)據(jù)的流動(dòng)和分發(fā)。

? PCWC:parallelcombinewithchild。

? PCWP:parallelcombinewithparent。

? P--‐>P: paralleltoparallel。

? P--‐>S: paralleltoSerial。

3. PQDistribute 列:數(shù)據(jù)的分發(fā)方式。此執(zhí)行計(jì)劃中,我們使用了broadcast 的方式,下面的章節(jié)

我會(huì)講述其他的分發(fā)方式。


HASH分發(fā)方式, 兩次數(shù)據(jù)分發(fā)

除了broadcast分發(fā)方式,另一種常見(jiàn)的并行分發(fā)方式為hash。為了觀察使用hash分發(fā)時(shí)sql的 執(zhí)行情況,我對(duì)sql使用pq_distributehint。

select /*+ monitor parallel(4)          leading(customer lineorder)          use_hash(lineorder)          pq_distribute(lineorder hash hash) */     sum(lo_revenue) from     lineorder, customer where     lo_custkey = c_custkey;

使用hash分發(fā)方式時(shí),sql的執(zhí)行時(shí)間為29s,dbtime為2.6m。相對(duì)于broadcast方式,sql的執(zhí)行時(shí)間和dbtime都增加了大約40%。

執(zhí)行計(jì)劃如下,執(zhí)行計(jì)劃為14行,增加了對(duì)lineorder的hash分發(fā),第11行的’PXSENDHASH’對(duì)3億行數(shù)據(jù)通過(guò)hash函數(shù)分發(fā),第10行的’PXRECEIVE’通過(guò)tablequeue1接收3億行數(shù)據(jù),這兩個(gè)操作消耗了38%的dbcpu。這就是為什么SQL執(zhí)行時(shí)間和dbtime變長(zhǎng)的原因。此時(shí),SQL的執(zhí)行順序?yàn)椋?/p>

  1. 紅色的PX進(jìn)程作為生產(chǎn)者,并行掃描customer(第8~9行),對(duì)于連接鍵c_custkey運(yùn)用函數(shù),根據(jù)每行記錄的hash值,通過(guò)tablequeue0,發(fā)給4個(gè)藍(lán)色消費(fèi)者的其中一個(gè)(第7行)。Hash分發(fā)方式并不會(huì)復(fù)制數(shù)據(jù),sqlmonitor報(bào)告的第6~9行,actualrows列都為1.5m。

  2. 紅色的PX進(jìn)程作為生產(chǎn)者,并行掃描li neorder(第12~13行),對(duì)于連接鍵lo_custkey運(yùn)用同樣的dhash函數(shù),通過(guò)tablequeue1,發(fā)給4個(gè)藍(lán)色消費(fèi)者的其中一個(gè)(第11行)。同樣的hash函數(shù)保證了customer和lineorder相同的連接鍵會(huì)發(fā)給同一個(gè)消費(fèi)者,保證hashjoin結(jié)果的正確。因?yàn)?億行數(shù)據(jù)都需要經(jīng)過(guò)hash函數(shù)計(jì)算,然后分發(fā)(這是進(jìn)程間的通信,或者需要通過(guò)RAC心跳網(wǎng)絡(luò)通信),這些巨大的額外開(kāi)銷,就是增加38%cpu的原因。

  3. 4個(gè)藍(lán)色的PX進(jìn)程作為消費(fèi)者接收了customer的1.5M行記錄(第 6 行),和lineorder的3億行記錄(第10行),進(jìn)行hash join(第5行),預(yù)聚合(第4行)。

  4. 4個(gè)藍(lán)色的PX進(jìn)程反過(guò)來(lái)作為生產(chǎn)者,通過(guò)table queue2,把聚合的數(shù)據(jù)發(fā)給消費(fèi)者QC(第3 行和第2行)。由QC對(duì)接收到4行記錄做最后的聚合, 然后返回給用戶(第1和0行)。

觀察sql monitor報(bào)告中Parallel標(biāo)簽下的信息,紅色的px進(jìn)程為實(shí)例1、2上的p002/p003進(jìn)程,藍(lán)色的PX進(jìn)程為p000/p001進(jìn)程。作為生產(chǎn)者的紅色PX進(jìn)程負(fù)責(zé)掃描事實(shí)表lineorder,對(duì)3億行數(shù)據(jù)進(jìn)行hash分發(fā),占了超過(guò)1/3的db time。


因?yàn)樯婕?億行數(shù)據(jù)的分發(fā)和接收,作為生產(chǎn)者的紅色PX進(jìn)程和作為消費(fèi)者的藍(lán)色PX進(jìn)程需要同時(shí)活躍,SQL monitor報(bào)告中的activity信息顯示大部分時(shí)間,AAS超過(guò)并行度4,意味這兩組PX進(jìn)程同時(shí)工作。不像replicate或者broadcast分發(fā)時(shí),AAS為4,只有一組PX進(jìn)程保持活躍。


SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- -------------- ---------- --------- ---------- 1 0 Producer 1 P002 299928364 1 0 Producer 1 P003 299954384 1 0 Producer 2 P002 300188788 1 0 Producer 2 P003 299951708 1 0 Consumer 1 P000 300005811 1 0 Consumer 1 P001 300005811 1 0 Consumer 2 P000 300005811 1 0 Consumer 2 P001 300005811 1 1 Producer 1 P000 1 1 1 Producer 1 P001 1 1 1 Producer 2 P000 1 1 1 Producer 2 P001 1 1 1 Consumer 1 QC 4 13 rows selected.

select /*+ monitor parallel(4)*/ sum(lo1.lo_revenue) from lineorder_hash42 lo1, lineorder_hash42 lo2 where lo1.lo_orderkey = lo2.lo_orderkey;

并行查詢之后,通過(guò)視圖V$PQ_TQSTAT,進(jìn)一步驗(yàn)證以上描述的執(zhí)行過(guò)程。并行執(zhí)行過(guò)程涉及3

個(gè)tablequeue0/1/2,V$PQ_TQSTAT包含21行記錄。

1. 實(shí)例1、2上的p002/p003進(jìn)程作為生產(chǎn)者,平均掃描customer的1/4記錄,然后通過(guò)tablequeue0(TQ_ID=0),發(fā)給作為消費(fèi)者的p000/p001進(jìn)程。發(fā)送和接收的customer記錄之和都為 1.5m。

? 發(fā)送的記錄數(shù):1500000= 365658+364899+375679+393764

? 接收的記錄數(shù):1500000= 374690+374924+375709+374677

2.  實(shí)例1、2上的p002/p0003進(jìn)程作為生產(chǎn)者,平均掃描lineorder的1/4記錄,通過(guò)table queue1(TQ_ID=1) ,發(fā)給作為消費(fèi)者的p000/p001進(jìn)程。發(fā)送和接收的lineorder 記錄之和都為300005811。

? 發(fā)送的記錄數(shù):300005811= 74987629+75053393+74979748+74985041

? 接收的記錄數(shù):300005811= 74873553+74968719+75102151+75061388

3. 實(shí)例1、2上的p000/p0001進(jìn)程作為生產(chǎn)者,通過(guò)tablequeue2(TQ_ID=2),把聚合的一條結(jié)果記 錄發(fā)給作為消費(fèi)者的QC。QC作為消費(fèi)者,接收了4行記錄。

SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- ---------------- ---------- --------- ---------- 1 0 Producer 1 P002 365658 1 0 Producer 1 P003 364899 1 0 Producer 2 P002 375679 1 0 Producer 2 P003 393764 1 0 Consumer 1 P000 374690 1 0 Consumer 1 P001 374924 1 0 Consumer 2 P000 375709 1 0 Consumer 2 P001 374677 1 1 Producer 1 P002 74987629 1 1 Producer 1 P003 75053393 1 1 Producer 2 P002 74979748 1 1 Producer 2 P003 74985041 1 1 Consumer 1 P000 74873553 1 1 Consumer 1 P001 74968719 1 1 Consumer 2 P000 75102151 1 1 Consumer 2 P001 75061388 1 2 Producer 1 P000 1 1 2 Producer 1 P001 1 1 2 Producer 2 P000 1 1 2 Producer 2 P001 1 1 2 Consumer 1 QC 4 21 rows selected.

小結(jié)

數(shù)組大小m,可以把錯(cuò)誤判斷的幾率控制在很小的范圍之內(nèi)。

我們觀察hash分發(fā)時(shí)sql的并行執(zhí)行過(guò)程。Hash分發(fā)與broadcast最大的區(qū)分在于對(duì)hashjoin的兩邊都進(jìn)行分發(fā)。這個(gè)例子中,對(duì)lineorder的hash分發(fā)會(huì)增加明顯的dbcpu。下一節(jié),我將使用另一個(gè)例子,說(shuō)明hash分發(fā)適用的場(chǎng)景。


Replicate,Broadcast和Hash的選擇

我們已經(jīng)測(cè)試過(guò)replicate,broadcast,和hash這三種分發(fā)方式。

  1. Replicate :每個(gè)PX進(jìn)程重復(fù)掃描hashjoin的左邊,buffercache被用來(lái)緩存hashjoin左邊的小表,減少重復(fù)掃描所需的物理讀。相對(duì)于broadcast分發(fā),replicate方式只需一組PX進(jìn)程。但是replicate不能替換broadcast分發(fā)。因?yàn)閞epli cate僅限于hashjoin左邊是表的情況,如果 hashjoin的左邊的結(jié)果集來(lái)自其他操作,比如join或者視圖,那么此時(shí)無(wú)法使用replicate。

  2. Broadcast分發(fā):作為生產(chǎn)者的PX進(jìn)程通過(guò)廣播的方式,把hashjoin左邊的結(jié)果集分發(fā)給每 個(gè)作為消費(fèi)者的PX進(jìn)程。一般適用于hashjoin左邊結(jié)果集比右邊小得多的場(chǎng)景,比如星型模型。

  3. Hash分發(fā)的本質(zhì):把hashjoin的左邊和右邊(兩個(gè)數(shù)據(jù)源),通過(guò)同樣hash函數(shù)重新分發(fā),切  分為N個(gè)工作單元(假設(shè)DoP=N),再進(jìn)行join ,目的是減少PX進(jìn)程進(jìn)行join操作時(shí),需要連接的數(shù)據(jù)量。Hash分發(fā)的代價(jià)需要對(duì)hashjoin的兩邊都進(jìn)行分發(fā)。對(duì)于customer連接lineorder的例子,因?yàn)榫S度表customer的數(shù)據(jù)量比事實(shí)表lineorder小得多,對(duì)customer進(jìn)行replicate或者broadcast分發(fā)顯然是更好的選擇,因?yàn)檫@兩種方式不用對(duì)lineorder進(jìn)行重新分發(fā)。如果是兩個(gè)大表join的話,join操作會(huì)是整個(gè)執(zhí)行計(jì)劃的瓶頸所在,hash分發(fā)是唯一合適的方式。為了減低join的代價(jià),對(duì)hashjoin左邊和右邊都進(jìn)行hash分發(fā)的代價(jià)是可以接受的。

Hash分發(fā),有時(shí)是唯一合理的選擇

我們使用lineorder上的自連接來(lái)演示,為什么有時(shí)hash分發(fā)是唯一合理的選擇。測(cè)試的SQL如 下:

select /*+ monitor parallel(4)*/ sum(lo1.lo_revenue) from lineorder lo1, lineorder lo2 where lo1.lo_orderkey = lo2.lo_orderkey;

SQL執(zhí)行時(shí)間為2.4分鐘,dbtime為10.5分鐘。


優(yōu)化器默認(rèn)選擇hash分發(fā)方式,執(zhí)行計(jì)劃為14行,結(jié)構(gòu)與之前的Hash分發(fā)的例子是一致的。不 同的是,第5行的hash join消耗了73%的db time,使用了9GB的臨時(shí)表空間,表空間的IO占12%的db time。大約15%的db time用于Lineorder的兩次hash分發(fā)和接收,相對(duì)上一個(gè)例子的占38%比例,這兩次HASH分發(fā)的整體影響降低了一倍多。


紅色的PX進(jìn)程為實(shí)例1、2上的p002/p003進(jìn)程,藍(lán)色的PX進(jìn)程為p000/p001進(jìn)程。作為生產(chǎn)者的紅色PX進(jìn)程占總db time的15%左右。


SQL執(zhí)行開(kāi)始,對(duì)lineorder兩次hash分發(fā)時(shí),AAS大于4,分發(fā)完成之后,只有藍(lán)色的PX進(jìn)程進(jìn)行 hash join操作,AAS=4。


從V$PQ_TQSTAT視圖可以確認(rèn),對(duì)于lineorder的存在兩次分發(fā),通過(guò)table queue0和1,作為消費(fèi)者的4個(gè)PX進(jìn)程接收到的兩次數(shù)據(jù)是一樣的,保證重新分發(fā)不會(huì)影響join結(jié)果的正確性。每個(gè)藍(lán)色PX 進(jìn)程需要hash join的左邊和右邊均為3億行數(shù)據(jù)的1/4,通過(guò)hash分發(fā),3億行記錄連接3億行記錄的工作平均的分配四個(gè)獨(dú)立PX進(jìn)程各自處理,每個(gè)PX進(jìn)程處理75M行記錄連接75M行記錄。

SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- -------------- ---------- --------- ---------- 1 0 Producer 1 P002 75055725 1 0 Producer 1 P003 74977459 1 0 Producer 2 P002 74995276 1 0 Producer 2 P003 74977351 1 0 Consumer 1 P000 74998419 1 0 Consumer 1 P001 74995836 1 0 Consumer 2 P000 74976974 1 0 Consumer 2 P001 75034582 1 1 Producer 1 P002 74986798 1 1 Producer 1 P003 74985268 1 1 Producer 2 P002 74984883 1 1 Producer 2 P003 75048862 1 1 Consumer 1 P000 74998419 1 1 Consumer 1 P001 74995836 1 1 Consumer 2 P000 74976974 1 1 Consumer 2 P001 75034582 1 2 Producer 1 P000 1 1 2 Producer 1 P001 1 1 2 Producer 2 P000 1 1 2 Producer 2 P001 1 1 2 Consumer 1 QC 4 21 rows selected.

使用 broadcast 分發(fā),糟糕的性能

對(duì)于lineorder,lineorder的自連接, 如果我們使用broadcast分發(fā),會(huì)出現(xiàn)什么情況呢?我們測(cè)試一下:

select /*+ monitor parallel(4) leading(lo1 lo2) use_hash(lo2) pq_distribute(lo2 broadcast none) */ 15 sum(lo1.lo_revenue) from lineorder lo1, lineorder lo2 where lo1.lo_orderkey = lo2.lo_orderkey;

使用broadcase分發(fā),SQL的執(zhí)行時(shí)間為5.9分鐘,db time為23.8分鐘。相比hash分發(fā),執(zhí)行時(shí)間和 db time都增加了接近1.5倍。


紅色的PX進(jìn)程作為生產(chǎn)者,對(duì)lineorder進(jìn)行并行掃描之后,3億行記錄通過(guò)tablequeue0廣播給4個(gè)作為消費(fèi)者的藍(lán)色PX進(jìn)程(第6~9行),相當(dāng)于復(fù)制了4份,每個(gè)藍(lán)色的PX進(jìn)程都接收了3億行記錄.這次broadcast分發(fā)消耗了11%的db time,因?yàn)樾枰啃杏涗泜鬏斀o每個(gè)藍(lán)色PX進(jìn)程,消耗的db cpu比使用hash分發(fā)時(shí)兩次hash分發(fā)所消耗的還多。

第5行的hash join的所消耗的臨時(shí)表空間上升到27GB,臨時(shí)表空間IO占的db time的38%。因?yàn)槊總€(gè)藍(lán)色PX進(jìn)程進(jìn)行hash join的數(shù)據(jù)變大了,hash join的左邊為3億行數(shù)據(jù),hash join的右邊為3億行記錄的1/4.


藍(lán)色PX進(jìn)程為消費(fèi)者負(fù)責(zé)hash join,所消耗的db time都大幅增加了。


hash join時(shí),臨時(shí)表空間讀等待事件’direct path read temp’明顯增加了。

V$PQ_TQSTAT的輸出中,實(shí)例1、2上的p000/p001進(jìn)程作為消費(fèi)者,都接收了3億行數(shù)據(jù),造成后續(xù)hash join的急劇變慢。Broadcast分發(fā)對(duì)hash join左邊進(jìn)行廣播的機(jī)制,決定了它不適合hash join兩邊都為大表的情況。

SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- -------------- ---------- --------- ---------- 1 0 Producer 1 P002 299928364 1 0 Producer 1 P003 299954384 1 0 Producer 2 P002 300188788 1 0 Producer 2 P003 299951708 1 0 Consumer 1 P000 300005811 1 0 Consumer 1 P001 300005811 1 0 Consumer 2 P000 300005811 1 0 Consumer 2 P001 300005811 1 1 Producer 1 P000 1 1 1 Producer 1 P001 1 1 1 Producer 2 P000 1 1 1 Producer 2 P001 1 1 1 Consumer 1 QC 4 13 rows selected.

小結(jié),Broadcast和Hash分發(fā)的陷阱

通過(guò)前一節(jié)和本節(jié)的例子,我們知道,如果選擇了不合理的分發(fā)方式,SQL執(zhí)行時(shí)性能會(huì)明顯下降

  1. 對(duì)于broadcast分發(fā):只對(duì)hash join的左邊進(jìn)行分發(fā),但是采用廣播分發(fā),hash join時(shí)左邊的數(shù)據(jù)量并沒(méi)有減少,如果hash join左邊的包含大量數(shù)據(jù),并行對(duì)hash join性能改善有限。對(duì)大量數(shù)據(jù)的broadcast分發(fā)也會(huì)消耗額外的db cpu,比如本節(jié)中l(wèi)ineorder自連接的例子。Replicate 同理。

  2. 對(duì)于hash分發(fā):對(duì)hash join的兩邊都進(jìn)行分發(fā),使每個(gè)PX進(jìn)程進(jìn)行hash join時(shí),左邊和右邊的數(shù)據(jù)量都為原始的1/N,N為并行度。Hash分發(fā)的潛在陷阱在于:

    ?兩次分發(fā),尤其對(duì)大表的分發(fā),可能帶來(lái)明顯的額外開(kāi)銷,比如前一節(jié)customer連接lineorder 的例子。使用Partition wise join可以消除分發(fā)的需要,后面會(huì)舉例說(shuō)明。

    ?如果數(shù)據(jù)存在傾斜,連接鍵上的少數(shù)值占了大部分的數(shù)據(jù),通過(guò)hash分發(fā),同一個(gè)鍵值的記錄會(huì)分發(fā)給同一個(gè)PX進(jìn)程,某一個(gè)PX進(jìn)程會(huì)處理大部分?jǐn)?shù)據(jù)的hash join,引起并行執(zhí)行傾斜。我會(huì)在后面的章節(jié)說(shuō)明這種情況和解決方法。

SQL解析時(shí),優(yōu)化器會(huì)根據(jù)hash join左邊和右邊估算的cardinality,并行度等信息,選擇具體何種分發(fā)方式。維護(hù)正確的統(tǒng)計(jì)信息,對(duì)于優(yōu)化器產(chǎn)生合理的并行執(zhí)行計(jì)劃是至關(guān)重要的。


Partition Wise Join,消除分發(fā)的額外開(kāi)銷

無(wú)論對(duì)于broadcast或者h(yuǎn)ash分發(fā),數(shù)據(jù)需要通過(guò)進(jìn)程或者節(jié)點(diǎn)之間通信的完成傳輸,分發(fā)的數(shù)據(jù)越多,消耗的db cpu越多。并行執(zhí)行時(shí),數(shù)據(jù)需要分發(fā),本質(zhì)上是因?yàn)镺racle采用share---everything的集中存儲(chǔ)架構(gòu),任何數(shù)據(jù)對(duì)每個(gè)實(shí)例的PX進(jìn)程都是共享的。為了對(duì)hash join操作分而治之,切分為N個(gè)獨(dú)立的工作單元(假設(shè) DoP=N),必須提前對(duì)數(shù)據(jù)重新分發(fā),數(shù)據(jù)的分發(fā)操作就是并行帶來(lái)的額外開(kāi)銷。

使用full或者partial partition wise join技術(shù),可以完全消除分發(fā)的額外開(kāi)銷,或者把這種開(kāi)銷降到最低。如果hash join有一邊在連接鍵上做hash分區(qū),那么優(yōu)化器可以選擇對(duì)分區(qū)表不分發(fā),因?yàn)閔ash分區(qū)已經(jīng)對(duì)數(shù)據(jù)完成切分,這只需要hash分發(fā)hash join的其中一邊,這是partial partition wise join。如果hash join的兩邊都在連接鍵上做了hash join分區(qū),那么每個(gè)PX進(jìn)程可以獨(dú)立的處理對(duì)等的hash分區(qū), 沒(méi)有數(shù)據(jù)需要分發(fā),這是full partition wise join。hash分區(qū)時(shí),hash join的工作單元就是對(duì)等hash分區(qū)包含的數(shù)據(jù)量,應(yīng)該控制每個(gè)分區(qū)的大小,hash join時(shí)就可能消除臨時(shí)表空間的使用,大幅減少所需的PGA。

Partition Wise Join,不需要數(shù)據(jù)分發(fā)。

如果在lineorder的列l(wèi)o_orderkey上做hash分區(qū),分區(qū)數(shù)為32個(gè)。每個(gè)分區(qū)的大小接近1G。

SEGMENT_NAME SEG_PART_NAME SEGMENT_TYPE SEG_MB ------------------ --------------- -------------------- ---------- LINEORDER_HASH32 SYS_P3345 TABLE PARTITION 960 LINEORDER_HASH32 SYS_P3344 TABLE PARTITION 960 ... LINEORDER_HASH32 SYS_P3315 TABLE PARTITION 960 LINEORDER_HASH32 SYS_P3314 TABLE PARTITION 960 ---------- 30720 32 rows selected.

使用lo_orderkey 連接時(shí),lineorder不需要再分發(fā)。我們繼續(xù)使用自連接的sql,演示full partition wise join。

select /*+ monitor parallel(4)*/ sum(lo1.lo_revenue) from lineorder_hash42 lo1, lineorder_hash42 lo2 where lo1.lo_orderkey = lo2.lo_orderkey;

此時(shí)sql執(zhí)行時(shí)間為1.6分鐘,dbtime 6分鐘;不分區(qū)使用hash分發(fā)時(shí),執(zhí)行時(shí)間為2.4分鐘,db time 10.5 分鐘。使用Partition Wise join快了三分之一。執(zhí)行計(jì)劃中只有一組藍(lán)色的PX進(jìn)程,不需要對(duì)數(shù)據(jù)進(jìn)行分發(fā)。因?yàn)閘ineorder_hash42的3億行數(shù)據(jù)被切分為32個(gè)分區(qū)。雖然并行度為4,每個(gè)PX進(jìn)程hash join時(shí),工作單元為一對(duì)匹配的hash分區(qū),兩邊的數(shù)據(jù)量都為3億的1/32。更小的工作單元,使整個(gè)hash join消耗的臨時(shí)表空間下降為  448MB。每個(gè)PX進(jìn)程消耗8對(duì)hash分區(qū),可以預(yù)見(jiàn),當(dāng)我們把并行度提高到8/16/32,每個(gè)PX進(jìn)程處理的hash分區(qū)對(duì)數(shù),應(yīng)該分別為4/2/1,sql執(zhí)行時(shí)間會(huì)線性的下降。


藍(lán)色的PX進(jìn)程為、的p000/p001進(jìn)程。每個(gè)PX進(jìn)程消耗的db time是平均的,每個(gè)PX進(jìn)程均處理了8對(duì)分區(qū)的掃描和hash join。


AAS絕大部分時(shí)間都為4。


唯一的數(shù)據(jù)連接為tablequeue0,每個(gè)PX進(jìn)程向QC發(fā)送一行記錄。

SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- ------------- ---------- ---------- ---------- 1 0 Producer 1 P000 1 1 0 Producer 1 P001 1 1 0 Producer 2 P000 1 1 0 Producer 2 P001 1 1 0 Consumer 1 QC 4 5 rows selected

當(dāng)DoP大于分區(qū)數(shù)時(shí),Partition Wise Join不會(huì)發(fā)生

當(dāng)并行執(zhí)行的DoP大于hash分區(qū)數(shù)時(shí),partition wise join不會(huì)發(fā)生,這時(shí)優(yōu)化器會(huì)使用 broadcast local的分發(fā)。使用DoP=64執(zhí)行同樣的sql:

select /*+ monitor parallel(64)*/ sum(lo1。lo_revenue) from lineorder_hash42 lo1, lineorder_hash42 lo2 where lo1。lo_orderkey = lo2。lo_orderkey

DoP=64,查詢執(zhí)行時(shí)間為15秒,db time為11.3分鐘。


執(zhí)行計(jì)劃中出現(xiàn)了兩組PX進(jìn)程。優(yōu)化器選擇對(duì)hash join的右邊進(jìn)行broadcast local分發(fā)。如果hash join的左邊比較小的話,broadcast local會(huì)發(fā)生在hash join的左邊。因?yàn)镈oP是分區(qū)數(shù)的兩倍,hash join兩邊的lineorder_hash74的每個(gè)分區(qū),由2個(gè)PX進(jìn)程共同處理。處理一對(duì)匹配分區(qū)的兩個(gè)藍(lán)色的PX進(jìn)程和兩個(gè)紅色的PX進(jìn)程,會(huì)處在同一個(gè)實(shí)例上。數(shù)據(jù)只會(huì)在同一個(gè)實(shí)例的PX進(jìn)程之間,不會(huì)跨實(shí)例傳輸,降低數(shù)據(jù)分發(fā)成本,這是broadcast local的含義。SQL的執(zhí)行順序如下:

  1. 以數(shù)據(jù)庫(kù)地址區(qū)間為單位,藍(lán)色的PX進(jìn)程并行掃描hash join左邊的lineorder_hash42(第7行),因?yàn)镈oP是分區(qū)數(shù)的兩倍,每個(gè)分區(qū)由兩個(gè)藍(lán)色PX進(jìn)程共同掃描,這兩個(gè)PX進(jìn)程在同一個(gè)實(shí)例上。每個(gè)藍(lán)色的PX進(jìn)程大約掃描每個(gè)分區(qū)一半的數(shù)據(jù),大約4.7M行記錄,并準(zhǔn)備好第5行hash join的build table。

  2. 紅色的PX進(jìn)程并行掃描hash join右邊的lineorder_hash42,每個(gè)紅色的PX進(jìn)程大概掃描4.7M行記錄,然后tablequeue0,以broadcast local的方式,分發(fā)給本實(shí)例兩個(gè)紅色的PX進(jìn)程(數(shù)據(jù)分發(fā)時(shí),映射到本實(shí)例某些PX進(jìn)程,避免跨節(jié)點(diǎn)傳輸?shù)奶匦?,稱為slaves mapping,除了broadcast local,還有hash local,random local等分發(fā)方式)。通過(guò)broadcast local分發(fā),數(shù)據(jù)量從300M行變成600M行。

  3. 每個(gè)藍(lán)色的PX進(jìn)程通過(guò)tablequeue0接收了大概9.4M行數(shù)據(jù),這是整個(gè)匹配分區(qū)的數(shù)據(jù)量。然后進(jìn)行hash join,以及之后的聚合操作。每個(gè)藍(lán)色的PX進(jìn)程hash join操作時(shí),左邊的數(shù)據(jù)量為lineorder_hash42的1/64(=1/DoP),右邊的數(shù)據(jù)為lineorder_hash42的1/32(=1/分區(qū)數(shù))。如果繼續(xù)提高DoP,只有hash join左邊的數(shù)據(jù)量減少,右邊的數(shù)據(jù)量并不會(huì)減少; 同時(shí),更多的PX進(jìn)程處理同一個(gè)分區(qū),會(huì)提高broadcast分發(fā)成本。所以當(dāng)DoP大于分區(qū)數(shù)時(shí),并行執(zhí)行的隨著DoP的提高,擴(kuò)展性并不好。

查看一個(gè)藍(lán)色的PX進(jìn)程,實(shí)例1p005進(jìn)程的執(zhí)行信息,可以確認(rèn)hash join的左邊為lineorder_hash42的1/64,hash join的右邊為lineorder_hash42的1/32。

小結(jié)

數(shù)據(jù)倉(cāng)庫(kù)設(shè)計(jì)時(shí),為了取得最佳的性能,應(yīng)該使用partition wise join和并行執(zhí)行的組合。在大表最常用的連接鍵上,進(jìn)行hash分區(qū),hash join時(shí)使優(yōu)化器有機(jī)會(huì)選擇partition wise join。Range-hash或者list-hash是常見(jiàn)的分區(qū)組合策略,一級(jí)分區(qū)根據(jù)業(yè)務(wù)特點(diǎn),利用時(shí)間范圍或者列表對(duì)數(shù)據(jù)做初步的切分,二級(jí)分區(qū)使用hash分區(qū)。查詢時(shí),對(duì)一級(jí)分區(qū)裁剪之后,優(yōu)化器可以選擇partition wise join。

設(shè)計(jì)partition wise join時(shí),應(yīng)該盡可能提高h(yuǎn)ash分區(qū)數(shù),控制每個(gè)分區(qū)的大小。Partition wise join時(shí),每對(duì)匹配的分區(qū)由一個(gè)PX進(jìn)程處理,如果分區(qū)數(shù)據(jù)太多,可能導(dǎo)致join操作時(shí)使用臨時(shí)空間,影響性能。另一方面,如果分區(qū)數(shù)太少,當(dāng)DoP大于分區(qū)數(shù)時(shí),partition wise join會(huì)失效,使用更大的DoP對(duì)性能改善非常有限。


數(shù)據(jù)傾斜對(duì)不同分發(fā)方式的影響

數(shù)據(jù)傾斜是指某一列上的大部分?jǐn)?shù)據(jù)都是少數(shù)熱門的值(Popular Value)。Hash join時(shí),如果hash join的右邊連接鍵上的數(shù)據(jù)是傾斜的,數(shù)據(jù)分發(fā)導(dǎo)致某個(gè)PX進(jìn)程需要處理所有熱門的數(shù)據(jù),拖長(zhǎng)sql執(zhí)行時(shí)間,這種情況稱為并行執(zhí)行傾斜。如果優(yōu)化器選擇了hash分發(fā),此時(shí)join兩邊的數(shù)據(jù)都進(jìn)行hash分發(fā),數(shù)據(jù)傾斜會(huì)導(dǎo)致執(zhí)行傾斜。同值記錄的hash值也是一樣的,會(huì)被分發(fā)到同一PX進(jìn)程進(jìn)行hash join。工作分配不均勻,某個(gè)不幸的PX進(jìn)程需要完成大部分的工作,消耗的db time會(huì)比其他PX進(jìn)程多,SQL執(zhí)行時(shí)間會(huì)因此被明顯延長(zhǎng)。對(duì)于replicate或者broadcast分發(fā),則不存在這種執(zhí)行傾斜的風(fēng)險(xiǎn),因?yàn)閔ash join右邊(一般為大表)的數(shù)據(jù)不用進(jìn)行分發(fā),PX進(jìn)程使用基于數(shù)據(jù)塊地址區(qū)間或者基于分區(qū)的granule,平均掃描hash join右邊的數(shù)據(jù),再進(jìn)行join操作。

為了演示數(shù)據(jù)傾斜和不同分發(fā)的關(guān)系,新建兩個(gè)表,customer_skew包含一條c_custkey=-1 的記錄,lineorder_skew 90%的記錄,兩億七千萬(wàn)行記錄lo_custkey=-1。

sid@SSB> select count(*) from customer_skew where c_custkey = -1; COUNT(*) ---------- 1 sid@SSB> select count(*) from customer_skew; COUNT(*) ---------- 1500000 sid@SSB> select count(*) from lineorder_skew where lo_custkey = -1; COUNT(*) ---------- 270007612 sid@SSB> select count(*) from lineorder_skew; COUNT(*) ---------- 21 300005811

Replicate方式,不受數(shù)據(jù)傾斜的影響

測(cè)試sql如下:

select /*+ monitor parallel(4) */ sum(lo_revenue) from lineorder_skew, customer_skew where lo_custkey = c_custkey;

SQL執(zhí)行時(shí)間為23秒,db time為1.5m。優(yōu)化器默認(rèn)的執(zhí)行計(jì)劃選擇replicate的方式,只需分配一組PX進(jìn)程,與broadcast分發(fā)的方式類似。每個(gè)藍(lán)色的PX進(jìn)程重復(fù)掃描customer,并行掃描lineorder_skew時(shí),是采用基于地址區(qū)間的granule為掃描單位,見(jiàn)第7行的’PX BLOCK ITERATOR’。


4個(gè)藍(lán)色的PX進(jìn)程消耗的db time是平均的,對(duì)于replicate方式,lineorder_skew的數(shù)據(jù)傾斜并沒(méi)有造成4個(gè)PX進(jìn)程的執(zhí)行傾斜。


當(dāng)優(yōu)化器使用replicate方式時(shí),可以通過(guò)執(zhí)行計(jì)劃中outline中的hint PQ_REPLICATE確認(rèn)。以下部分dbms_xplan。display_cursor輸出沒(méi)有顯示,只顯示outline數(shù)據(jù)。

select * from table(dbms_xplan.display_cursor('77457qc9a324k',0,’outline’)); Plan hash value: 4050055921 ... Outline Data ------------- /*+ BEGIN_OUTLINE_DATA 22 IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('12.1.0.2') DB_VERSION('12.1.0.2') …… ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "CUSTOMER_SKEW"@"SEL$1") FULL(@"SEL$1" "LINEORDER_SKEW"@"SEL$1") LEADING(@"SEL$1" "CUSTOMER_SKEW"@"SEL$1" "LINEORDER_SKEW"@"SEL$1") USE_HASH(@"SEL$1" "LINEORDER_SKEW"@"SEL$1") PQ_DISTRIBUTE(@"SEL$1" "LINEORDER_SKEW"@"SEL$1" BROADCAST NONE) PQ_REPLICATE(@"SEL$1" "LINEORDER_SKEW"@"SEL$1") END_OUTLINE_DATA */

Hash分發(fā),數(shù)據(jù)傾斜造成執(zhí)行傾斜

通過(guò)hint使用hash分發(fā),測(cè)試sql如下:

select /*+ monitor parallel(4) leading(customer_skew lineorder_skew) use_hash(lineorder_skew) pq_distribute(lineorder_skew hash hash) */ sum(lo_revenue) from lineorder_skew, customer_skew where lo_custkey = c_custkey;

使用hash分發(fā),SQL執(zhí)行時(shí)間為58秒,dbtime 2.1分鐘。對(duì)于replicate時(shí)sql執(zhí)行時(shí)間23秒,dbtime 1.5分鐘。有趣的是,整個(gè)sql消耗的db time只增加了37秒,而執(zhí)行時(shí)間確增加了35秒,意味著所增加的dbtime并不是平均到每個(gè)PX進(jìn)程的。如果增加的dbtime平均到每個(gè)PX進(jìn)程,而且并行執(zhí)行沒(méi)有傾斜的話,那么sql執(zhí)行時(shí)間應(yīng)該增加37/4,約9秒,而不是現(xiàn)在的35秒。紅色的PX 進(jìn)程作為生產(chǎn)者,分別對(duì)customer_skew和lineorder_skew  完成并行掃描并通過(guò)tablequeue0/1,hash分發(fā)給藍(lán)色的PX進(jìn)程。對(duì)lineorder_skew的分發(fā),占了45%的db cpu。


實(shí)例2的藍(lán)色PX進(jìn)程p001消耗了57.1秒的dbtime,sql執(zhí)行時(shí)間58秒,這個(gè)PX進(jìn)程在sql執(zhí) 行過(guò)程中一直是活躍狀態(tài)??梢灶A(yù)見(jiàn),lineorder_skew所有l(wèi)o_custkey=-1的數(shù)據(jù)都分發(fā)到這個(gè)進(jìn)程處理。而作為生產(chǎn)者的紅色PX進(jìn)程,負(fù)責(zé)掃描lineorder_skew并進(jìn)行分發(fā),它們的工作量是平均的。


大部分時(shí)候AAS=2,只有實(shí)例2的p001進(jìn)程不斷的從4個(gè)生產(chǎn)者接收數(shù)據(jù)并進(jìn)行hash join。


從V$PQ_TQSTAT視圖我們可以確認(rèn),對(duì)hash join右邊分發(fā)時(shí),通過(guò)tablequeue1,作為消費(fèi)者的實(shí)例2的P001,接收了兩億七千多萬(wàn)的數(shù)據(jù)。這就是該P(yáng)X進(jìn)程在整個(gè)sql執(zhí)行過(guò)程中一直保持活躍的原因。

SELECT dfo_number, tq_id, server_type, instance, process, num_rows FROM V$PQ_TQSTAT ORDER BY dfo_number DESC, tq_id, server_type desc, instance, process; DFO_NUMBER TQ_ID SERVER_TYPE INSTANCE PROCESS NUM_ROWS ---------- ---------- ------------- ---------- ---------- ---------- 1 0 Producer 1 P004 375754 1 0 Producer 1 P005 365410 1 0 Producer 2 P003 393069 1 0 Producer 2 P004 365767 1 0 Consumer 1 P002 375709 1 0 Consumer 1 P003 374677 1 0 Consumer 2 P001 374690 1 0 Consumer 2 P002 374924 1 1 Producer 1 P004 75234478 1 1 Producer 1 P005 74926098 1 1 Producer 2 P003 74923913 1 1 Producer 2 P004 74921322 1 1 Consumer 1 P002 7497409 1 1 Consumer 1 P003 7467378 1 1 Consumer 2 P001 277538575 1 1 Consumer 2 P002 7502449 24 1 2 Producer 1 P002 1 1 2 Producer 1 P003 1 1 2 Producer 2 P001 1 1 2 Producer 2 P002 1 1 2 Consumer 1 QC 4 21 rows selected.

12c的sqlmonitor報(bào)告作了增強(qiáng),并行執(zhí)行傾斜時(shí),包含了消耗最大的PX進(jìn)程的采樣信息。在plan statistics頁(yè)面,下拉菜單選擇’Parallel Server 3(instance 2,p001)’, 從執(zhí)行計(jì)劃的第10行,‘PX RECEIVE’,以及Actual Rows列的數(shù)據(jù)278M,也可以確認(rèn)實(shí)例2的p001進(jìn)程接收了兩億七千多萬(wàn)數(shù)據(jù)。


小節(jié)

對(duì)于實(shí)際的應(yīng)用,處理數(shù)據(jù)傾斜是一個(gè)復(fù)雜的主題。比如在傾斜列上使用綁定變量進(jìn)行過(guò)濾,綁定變量窺視(bind peeking)可能造成執(zhí)行計(jì)劃不穩(wěn)定。本節(jié)討論了數(shù)據(jù)傾斜對(duì)不同分發(fā)方式的帶來(lái)影響:

  1. 通常,replicate或者broadcast分發(fā)不受數(shù)據(jù)傾斜的影響。

  2. 對(duì)于hash分發(fā),hash join兩邊連接鍵的最熱門數(shù)據(jù),會(huì)被分發(fā)到同一PX進(jìn)程進(jìn)行join操作,容易造成明顯的并行執(zhí)行傾斜。

  3. 12c引入adaptive分發(fā),可以解決hash分發(fā)時(shí)并行執(zhí)行傾斜的問(wèn)題。我將在下一篇文章” 深入理解Oracle的并行執(zhí)行傾斜(下)”演示adaptive分發(fā)這個(gè)新特性。

HASH JOIN BUFFERED,連續(xù)hash分發(fā)時(shí)執(zhí)行計(jì)劃中的阻塞點(diǎn)

到目前為止,所有的測(cè)試只涉及兩個(gè)表的連接。如果多于兩個(gè)表,就需要至少兩次的hash join,數(shù)據(jù)分發(fā)次數(shù)變多,生產(chǎn)者消費(fèi)者的角色可能互換,執(zhí)行計(jì)劃將不可避免變得復(fù)雜。執(zhí)行路徑變長(zhǎng),為了保證并行執(zhí)行的正常進(jìn)行,執(zhí)行計(jì)劃可能會(huì)插入相應(yīng)的阻塞點(diǎn),在hash join時(shí),把符合join條件的數(shù)據(jù)緩存到臨時(shí)表,暫停數(shù)據(jù)繼續(xù)分發(fā)。本節(jié)我使用一個(gè)三表連接的sql來(lái)說(shuō)明連續(xù)hash join時(shí),不同分發(fā)方式的不同行為。

使用Broadcast分發(fā),沒(méi)有阻塞點(diǎn)。

測(cè)試三個(gè)表連接的sql如下,加入part表,使用hint讓優(yōu)化器兩次hash join都使用broadcast分發(fā)。Replicate SQL查詢性能類似。

select /*+ monitor parallel(4) LEADING(CUSTOMER LINEORDER PART) USE_HASH(LINEORDER) USE_HASH(PART) SWAP_JOIN_INPUTS(PART) PQ_DISTRIBUTE(PART NONE BROADCAST) NO_PQ_REPLICATE(PART) PQ_DISTRIBUTE(LINEORDER BROADCAST NONE) NO_PQ_REPLICATE(LINEORDER) 25 */ sum(lo_revenue) from lineorder, customer, part where lo_custkey = c_custkey and lo_partkey = p_partkey;

SQL執(zhí)行時(shí)間為42秒,dbtime為2.6分鐘。

AAS=(sql db time)/(sql 執(zhí)行時(shí)間)=(2.6*60)/42=3.7,接近4,說(shuō)明4個(gè)PX進(jìn)程基本一直保持活躍。


執(zhí)行計(jì)劃是一顆完美的右深樹(shù),這是星型模型查詢時(shí)執(zhí)行計(jì)劃的典型形式。生產(chǎn)者對(duì)兩個(gè)維度進(jìn)行broadcast分發(fā),消費(fèi)者接受數(shù)據(jù)之后準(zhǔn)備好兩次hash join的build table, 最后掃描事實(shí)表,并進(jìn)行hash join。我們通過(guò)跟隨table queue順序的原則,閱讀這個(gè)執(zhí)行計(jì)劃。

  1. 紅色PX進(jìn)程作為生產(chǎn)者并行掃描part,通過(guò)tablequeue0廣播給每個(gè)藍(lán)色的消費(fèi)者PX進(jìn)程 (第7~9行)。每個(gè)藍(lán)色的PX進(jìn)程接收part的完整數(shù)據(jù)(第6行),1.2M行記錄,并準(zhǔn)備好第5行hash join的build table。

  2. 紅色PX進(jìn)程作為生產(chǎn)者并行掃描customer,通過(guò)tablequeue1廣播broadcast給每個(gè)藍(lán)色的 消費(fèi)者PX進(jìn)程(第12~14行)。每個(gè)藍(lán)色的PX進(jìn)程接收customer的完整數(shù)據(jù)(第11行),1.5M行記錄,并準(zhǔn)備好第10行hash join的build table。

  3. 藍(lán)色的PX進(jìn)程并行掃描事實(shí)表lineorder,對(duì)每條符合掃描條件(如果sql語(yǔ)句包含對(duì)lineorder的過(guò)濾條件)的3億行記錄,進(jìn)行第10行的hash join,對(duì)于每一條通過(guò)第10行的 hash join的記錄,馬上進(jìn)行第5行的hash join,接著再進(jìn)行聚合。從sql monitor報(bào)告的 Timeline列信息,對(duì)lineorder的掃描和兩個(gè)hash join操作是同時(shí)進(jìn)行的。執(zhí)行計(jì)劃中沒(méi)有阻塞點(diǎn),數(shù)據(jù)在執(zhí)行路徑上的流動(dòng)不需要停下來(lái)等待。大部分的db cpu消耗在兩次hash join操作。最優(yōu)化的執(zhí)行計(jì)劃,意味著經(jīng)過(guò)每個(gè)hash join的數(shù)據(jù)越少越好。對(duì)于這類執(zhí)行計(jì)劃,你需要確保優(yōu)化器把最能過(guò)濾數(shù)據(jù)的join,放在最接近事實(shí)表的位置執(zhí)行。


連續(xù)hash分發(fā),執(zhí)行計(jì)劃出現(xiàn)阻塞點(diǎn)

使用以下hints,強(qiáng)制SQL使用hash分發(fā)。

select /*+ monitor parallel(4) LEADING(CUSTOMER LINEORDER PART) USE_HASH(LINEORDER) USE_HASH(PART) SWAP_JOIN_INPUTS(PART) PQ_DISTRIBUTE(PART HASH HASH) 26 PQ_DISTRIBUTE(LINEORDER HASH HASH) */ sum(lo_revenue) from lineorder, customer, part where lo_custkey = c_custkey and lo_partkey = p_partkey;

SQL執(zhí)行時(shí)間為1.5分鐘,dbtime為8.1分鐘。相對(duì)于增加了14GB的IO操作。


連續(xù)兩次hash join都使用HASH分發(fā),每次hash join左右兩邊都需要分發(fā),PX進(jìn)程之間發(fā)生4次數(shù)據(jù)分發(fā)。執(zhí)行計(jì)劃中最顯著的地方來(lái)自第12行的HASH JOIN BUFFERED,這是一個(gè)阻塞性的操作。下面,我們依然通過(guò)跟隨table queue順序的原則,閱讀執(zhí)行計(jì)劃,并解析為什么出現(xiàn)HASH JOIN BUFFERED這個(gè)阻塞操作,而不是一般的HASH JOIN。

1. 藍(lán)色的PX進(jìn)程作為生產(chǎn)者,并行掃描customer,通過(guò)tablequeue0,hash分發(fā)給作為消費(fèi)者的紅色PX進(jìn)程(第14~16行)。每個(gè)紅色的PX進(jìn)程接收了1/4的customer的數(shù)據(jù)(第13行), 大約為370k行記錄,并準(zhǔn)備好第12行‘HASH JOIN BUFFERED’的build table。與broadcast分發(fā)區(qū)別的是,此時(shí)執(zhí)行計(jì)劃是從第16行,掃描靠近lineorder的customer開(kāi)始的,而不是從第一個(gè)沒(méi)有’孩子’的操作(第9行掃描part)開(kāi)始的。這是hash分發(fā)和串行執(zhí)行計(jì)劃以及broadcast分發(fā)不同的地方。

2. 藍(lán)色的PX進(jìn)程作為生產(chǎn)者,并行掃描lineorder,通過(guò)tablequeue1,hash分發(fā)作為消費(fèi)者的紅色PX進(jìn)程(第18~20行)。每個(gè)紅色PX進(jìn)程接收了1/4的lineorder數(shù)據(jù)(第17行),大約75M行記錄。每個(gè)紅色PX進(jìn)程在接收通過(guò)tablequeue1接收數(shù)據(jù)的同時(shí),進(jìn)行第12行的hash join,并把join的結(jié)果集在PGA中作緩存,使數(shù)據(jù)暫時(shí)不要繼續(xù)往上流動(dòng)。如果結(jié)果集過(guò) 大的話,需要把數(shù)據(jù)暫存到臨時(shí)空間,比如我們這個(gè)例子,用了7GB的臨時(shí)空間。你可以理解為把join的結(jié)果集暫存到一個(gè)臨時(shí)表。那么,為什么執(zhí)行計(jì)劃需要在這里插入一個(gè)阻塞點(diǎn),阻止數(shù)據(jù)繼續(xù)往上流動(dòng)呢?

這里涉及生產(chǎn)者消費(fèi)者模型的核心:同一棵DFO樹(shù)中,最多只能有兩組PX進(jìn)程,一個(gè)數(shù)據(jù)分發(fā)要求兩組PX進(jìn)程協(xié)同工作; 這意味著同一時(shí)刻,兩組PX進(jìn)程之間,最多只能存在一個(gè)活躍的數(shù)據(jù)分發(fā),一組作為生產(chǎn)者發(fā)送數(shù)據(jù),一組作為消費(fèi)者接收數(shù)據(jù),每個(gè)PX進(jìn)程只能扮演其中一種角色,不能同時(shí)扮演兩種角色。當(dāng)紅色的PX進(jìn)程通過(guò)tablequeue1向藍(lán)色的PX進(jìn)程分發(fā)lineorder數(shù)據(jù),同時(shí),藍(lán)色的PX進(jìn)程正在接收l(shuí)ineorder數(shù)據(jù),并進(jìn)行hash join。觀察timeline列的時(shí)間軸信息,第12,17~20行是同時(shí)進(jìn)行的。 但是此時(shí)紅色的PX進(jìn)程不能反過(guò)來(lái)作為生產(chǎn)者,把hash join的結(jié)果分發(fā)給藍(lán)色進(jìn)程,因?yàn)榇藭r(shí)有兩個(gè)限制:

? 藍(lán)色的PX進(jìn)程作為生產(chǎn)者,正忙著掃描lineorder;此時(shí),無(wú)法反過(guò)來(lái)作為消費(fèi)者,接收來(lái)自紅色PX進(jìn)程的數(shù)據(jù)。

? 第5行hash jon操作的build table還沒(méi)準(zhǔn)備好,這時(shí)表part甚至還沒(méi)被掃描。

所以O(shè)racle需要在第12行hash join這個(gè)位置插入一個(gè)阻塞點(diǎn),變成HASH JOIN BUFFER操作,把join的結(jié)果集緩存起來(lái)。當(dāng)藍(lán)色的PX進(jìn)程完成對(duì)lineorder的掃描和分發(fā),紅色的PX進(jìn)程完成第12行的hash join并把結(jié)果完全暫存到臨時(shí)空間之后。Tablequeue2的數(shù)據(jù)分發(fā)就開(kāi)始了。

3. 紅色的PX進(jìn)程作為生產(chǎn)者,并行掃描part,通過(guò)tablequeue2,分發(fā)給作為消費(fèi)者的藍(lán)色PX進(jìn)程(第7~9行)。每個(gè)藍(lán)色PX進(jìn)程接收了1/4的part數(shù)據(jù)(第6行),大概300k行記錄,并準(zhǔn)備好第5行hash join的build table。

4. 紅色的PX進(jìn)程作為生產(chǎn)者,把在第12行”HASH JOIN BUFFERED”操作,存在臨時(shí)空間的對(duì)于customer和lineorder連接的結(jié)果集,讀出來(lái),通過(guò)table queue 3,分發(fā)給藍(lán)色的PX進(jìn)程(第11~12行)?!癏ASH JOIN BUFFERED”這個(gè)操作使用了7GB的臨時(shí)空間,寫IO7GB,讀IO 7GB,IO總量為 14GB。

5. 每個(gè)藍(lán)色的PX進(jìn)程作為消費(fèi)者,接收了大約75M行記錄。對(duì)于通過(guò)tablequeue3接收到的 數(shù)據(jù),同時(shí)進(jìn)行第5行的hash join,并且通過(guò)join操作的數(shù)據(jù)進(jìn)行第4行的聚合操作。當(dāng)tablequeue3上的數(shù)據(jù)分發(fā)結(jié)束,每個(gè)藍(lán)色的PX進(jìn)程完成hash join和聚合操作之后,再把各自的聚合結(jié)果,一行記錄,通過(guò)tablequeue4,分發(fā)給QC(第3~5行)。QC完成最后的聚合,返回給客戶端。


小結(jié)

因?yàn)槭褂眯切湍P蜏y(cè)試,這個(gè)例子使用Broadcast分發(fā)或者replicate才是合理的。實(shí)際應(yīng)用中,連續(xù)的hash分發(fā)并不一定會(huì)出現(xiàn)HASH JOIN BUFFERED這個(gè)阻塞點(diǎn),如果查詢涉及的表都較小,一般不會(huì)出現(xiàn)HASH JON BUFFERED。即使執(zhí)行計(jì)劃中出現(xiàn)BUFFER SORT,HASH JOIN BUFFERED等阻塞操作,也不意味著執(zhí)行計(jì)劃不是最優(yōu)的。如果sql性能不理想,HASH JOIN BUFFERED操作消耗了大部分的CPU和大量臨時(shí)空間,通過(guò)sql monitor報(bào)告,你可以判斷這是否是合理的:

  1. 檢查estimated rows和actual rows這兩列,確定優(yōu)化器對(duì)hash Join左右兩邊cardinality估算是否出現(xiàn)偏差,所以選擇hash分發(fā)。

  2. 同樣檢查hash join操作的estimated rows和actual rows這兩列,優(yōu)化器對(duì)hash join結(jié)果集cardinality的估算是否合理。優(yōu)化器會(huì)把hash join的兩邊視為獨(dú)立事件,對(duì)join結(jié)果集cardinality的估算可能過(guò)于保守,estimate rows偏小。對(duì)于星型模型的一種典型情況:如果多個(gè)維度表參與連接,執(zhí)行路徑很長(zhǎng),一開(kāi)始維度表的分發(fā)方式為broadcast,事實(shí)表不用分發(fā),經(jīng)過(guò)幾次join之后,結(jié)果集cardinality下降很快,后續(xù)hash join兩邊的estimated rows接近,導(dǎo)致優(yōu)化器選擇hash分發(fā)。

  3. 通過(guò)檢查每個(gè)join所過(guò)濾的數(shù)據(jù)比例,確定優(yōu)化器是否把最有效過(guò)濾數(shù)據(jù)的join最先執(zhí)行,保證在執(zhí)行路徑上流動(dòng)的數(shù)據(jù)量最少。

Hash join和布隆過(guò)濾

布隆過(guò)濾在并行執(zhí)行計(jì)劃中的使用非常普遍,我將在本章節(jié)解釋這一數(shù)據(jù)結(jié)構(gòu)及其作用。 從11.2版本開(kāi)始,串行執(zhí)行的sql也可以使用布隆過(guò)濾。

關(guān)于布隆過(guò)濾

布隆過(guò)濾是一種內(nèi)存數(shù)據(jù)結(jié)構(gòu),用于判斷某個(gè)元素是否屬于一個(gè)集合。布隆過(guò)濾的工作原理圖2如下:

引用自維基百科:http://en.wikipedia.org/wiki/Bloom_filter


如圖,布隆過(guò)濾是一個(gè)簡(jiǎn)單的bit數(shù)組,需要定義兩個(gè)變量:

  1. m:數(shù)組的大小,這個(gè)例子中,m=18.

  2. k:hash函數(shù)的個(gè)數(shù),這個(gè)例子中,k=3,

一個(gè)空的布隆過(guò)濾所有bit都為0。增加一個(gè)元素時(shí),該元素需要經(jīng)過(guò)三個(gè)hash函數(shù)計(jì)算, 得到3個(gè)hash值,把數(shù)組中這三個(gè)位置都置為1。集合{x,y,z}的3個(gè)元素,分布通過(guò)三次hash計(jì)算,把數(shù)組9個(gè)位置設(shè)置為1。判斷某個(gè)元素是否屬于一個(gè)集合,比如圖中的w, 只需對(duì)w進(jìn)行三次hash計(jì)算產(chǎn)生三個(gè)值,右邊的位置在數(shù)組中不命中,該位置為0,可以確定,w不在{x,y,z}這個(gè)集合。由于存在hash碰撞,布隆過(guò)濾的判斷會(huì)過(guò)于樂(lè)觀(false positive),可能存在元素不屬于{x,y,z},但是通過(guò)hash計(jì)算之后三個(gè)位置都命中,被錯(cuò)誤認(rèn)定為屬于{x,y,z}。根據(jù)集合元素的個(gè)數(shù),合理的設(shè)置數(shù)組大小m,可以把錯(cuò)誤判斷的幾率控制在很小的范圍之內(nèi)。

布隆過(guò)濾對(duì)hash join性能的改進(jìn)

布隆過(guò)濾的優(yōu)勢(shì)在于使用的很少內(nèi)存,就可以過(guò)濾大部分的數(shù)據(jù)。如果hash join的左邊包含過(guò)濾條件,優(yōu)化器可能選擇對(duì)hash join左邊的數(shù)據(jù)集生成布隆過(guò)濾,在掃描hash join右邊時(shí)使用這個(gè)布隆布隆作為過(guò)濾條件,第一時(shí)間把絕大部分不滿足join條件數(shù)據(jù)排除。減少數(shù)據(jù)分發(fā)和join操作所處理的數(shù)據(jù)量,提高性能。

使用布隆過(guò)濾時(shí)的性能

使用布隆過(guò)濾時(shí)的性能對(duì)customer使用c_nation=’CHINA’條件,只計(jì)算來(lái)自中國(guó)地區(qū)的客戶訂單的利潤(rùn)總和。我們觀察使用布隆過(guò)濾和不使用布隆過(guò)濾時(shí)性能的差別。

select /*+ monitor parallel(4)*/ sum(lo_revenue) from lineorder, customer                     where lo_custkey = c_custkey and c_nation = 'CHINA';

SQL執(zhí)行時(shí)間為1秒,dbtime為7.9 秒。優(yōu)化器默認(rèn)選擇replicate的方式。執(zhí)行計(jì)劃中多了JOIN FILTER CREATE和JOIN FILTER USE這兩個(gè)操作。SQL的執(zhí)行順序?yàn)槊總€(gè)PX進(jìn)程重復(fù)掃描customer表(第7行),對(duì)符合c_nation=’CHINA’數(shù)據(jù)集,60K(240K/4)行記錄,在c_custkey列生成布隆過(guò)濾:BF0000(第6行JOIN FILTER CREATE)。在掃描lineorder時(shí)使用這個(gè)布隆過(guò)濾(第8行JOIN FILTER USE)。雖然lineorder總行數(shù)為300M,sql沒(méi)有過(guò)濾條件,只使用布隆過(guò)濾,掃描之后只返回28M行記錄,其他272M行記錄被過(guò)濾掉了。每個(gè)PX進(jìn)程在hash join操作時(shí),只需處理60K行customer記錄和7M(28M/4)行l(wèi)ineorder記錄的連接,大大降低join操作的成本。對(duì)于Exadata,Smart Scan支持布隆過(guò)濾卸載到存儲(chǔ)節(jié)點(diǎn),存儲(chǔ)節(jié)點(diǎn)掃描lineorder時(shí),使用布隆過(guò)濾排除272M行記錄,對(duì)于符合條件的數(shù)據(jù),把不需要的列也去掉。Cell offload Efficiency=98%,意味著只有30GB的2%從存儲(chǔ)節(jié)點(diǎn)返回給PX進(jìn)程。如果不使用布隆過(guò)濾,Cell Offload Efficieny不會(huì)高達(dá)98%,我們將在下個(gè)例子看到。對(duì)于非Exadata平臺(tái),由于沒(méi)有Smart Scan特性,數(shù)據(jù)的過(guò)濾操作需要由PX進(jìn)程完成,布隆過(guò)濾的效果不會(huì)這么明顯。12C的新特性Database In--‐memory,支持掃描列式存儲(chǔ)的內(nèi)存數(shù)據(jù)時(shí),使用布隆過(guò)濾。


執(zhí)行計(jì)劃中出現(xiàn)第10行對(duì)LINEORDER的掃描時(shí),使用了布隆過(guò)濾條件:SYS_OP_BLOOM_FILTER(:BF0000,"LO_CUSTKEY")


不使用布隆過(guò)濾時(shí)的性能

接著,我們通過(guò)hint NO_PX_JOIN_FILTER,禁用布隆過(guò)濾,觀察此時(shí)的sql執(zhí)行性能。

select /*+ monitor parallel(4) NO_PX_JOIN_FILTER(LINEORDER)*/ sum(lo_revenue) from lineorder, customer where lo_custkey = c_custkey and c_nation = 'CHINA';

SQL執(zhí)行時(shí)間為9秒,dbtime為33.7秒。比使用布隆過(guò)濾時(shí),性能下降明顯。優(yōu)化器依然選擇replicate的方式,執(zhí)行計(jì)劃中沒(méi)有PX JOIN CREATE和PX JOIN USE操作。db time增加為原來(lái)4倍的原因:

  1. 當(dāng)PX掃描lineorder時(shí),返回300M行記錄。沒(méi)有布隆過(guò)濾作為條件,每個(gè)PX進(jìn)程需要從存儲(chǔ)節(jié)點(diǎn)接收75M行記錄。

  2. 進(jìn)行第5行的hash join操作時(shí),每個(gè)PX進(jìn)程需要連接60k行customer記錄和75M行l(wèi)ineorder記錄。Join操作的成本大幅增加。

由于沒(méi)有布隆過(guò)濾,Cell Offload Efficiency下降為83%。


HASH分發(fā)時(shí)布隆過(guò)濾的生成,傳輸,合并與使用

我們通過(guò)hint強(qiáng)制使用hash分發(fā),觀察此時(shí)sql執(zhí)行計(jì)劃中布隆過(guò)濾的生成和使用。

select /*+ monitor parallel(4) leading(customer lineorder) use_hash(lineorder) pq_distribute(lineorder hash hash) */ sum(lo_revenue) from lineorder, customer where lo_custkey = c_custkey and c_nation = 'CHINA';

此時(shí)sql執(zhí)行時(shí)間為4秒,db time為19.4秒。執(zhí)行計(jì)劃第6行為JOIN FILTER CREATE; 第13行為JOIN FILTER USE。此例,PX 進(jìn)程分布在多個(gè)RAC兩個(gè)實(shí)例,Hash分發(fā)時(shí)涉及布隆過(guò)濾的生成,傳輸,合并和使用,較為復(fù)雜,具體過(guò)程如下:

  1. 布隆過(guò)濾的產(chǎn)生:4個(gè)藍(lán)色的PX進(jìn)程作為消費(fèi)者,通過(guò)tablequeue0,接收紅色的PX進(jìn)程hash分發(fā)的customer數(shù)據(jù),每個(gè)藍(lán)色的PX進(jìn)程接收15K行記錄。接收customer記錄的同時(shí),實(shí)例1的兩個(gè)藍(lán)色PX進(jìn)程在SGA共同生成一個(gè)布隆過(guò)濾,假設(shè)為B1; 實(shí)例2的兩個(gè)藍(lán)色PX進(jìn)程在SGA共同生成一個(gè)布隆過(guò)濾,假設(shè)為B2。因?yàn)槲挥赟GA中,布隆過(guò)濾B1對(duì)于實(shí)例1的 兩個(gè)紅色的PX進(jìn)程是可見(jiàn)的,同樣,B2對(duì)于實(shí)例2的兩個(gè)紅色PX進(jìn)程也是可見(jiàn)的。

  2. 布隆過(guò)濾的傳輸:當(dāng)紅色的PX進(jìn)程完成對(duì)hash join左邊customer的掃描,就會(huì)觸發(fā)布隆過(guò)濾B1/B2的傳輸。實(shí)例1的紅色PX進(jìn)程把B1發(fā)給實(shí)例2的藍(lán)色PX進(jìn)程; 實(shí)例2的紅色PX進(jìn)程把B2發(fā)給實(shí)例1的藍(lán)色PX進(jìn)程。

  3. 布隆過(guò)濾的合并:實(shí)例1的藍(lán)色PX進(jìn)程合并B1和接收到的B2; 實(shí)例2的藍(lán)色PX進(jìn)程合并B2和接收到的B1。合并之后,實(shí)例1和2產(chǎn)生相同布隆過(guò)濾。

  4. 布隆過(guò)濾的使用:實(shí)例1和2的4個(gè)紅色的PX進(jìn)程作為生產(chǎn)者,并行掃描lineorder時(shí)使用 合并之后的布隆過(guò)濾進(jìn)行過(guò)濾。Lineorder過(guò)濾之后為49M行記錄,此時(shí)的布隆過(guò)濾似乎沒(méi)有replicate時(shí)的有效。Cell Offloadload Efficiency為97%。

如果并行執(zhí)行只在一個(gè)實(shí)例,則紅色的PX進(jìn)程不需要對(duì)布隆過(guò)濾進(jìn)行傳輸,藍(lán)色的PX進(jìn)程也無(wú)需對(duì)布隆過(guò)濾進(jìn)行合并。

因?yàn)閔ash join的成本大大降低了,對(duì)于lineorder 49M行記錄的hash分發(fā),成為明顯的平均,占53%的db time。


小結(jié)

本節(jié)闡述了布隆過(guò)濾的原理,以及在Oracle中的一個(gè)典型應(yīng)用:對(duì)hash join性能的提升。布隆過(guò)濾的本質(zhì)在于把hash join的連接操作提前了,對(duì)hash join右邊掃描時(shí),就第一時(shí)間把不符合join條件的大部分?jǐn)?shù)據(jù)過(guò)濾掉。大大降低后續(xù)數(shù)據(jù)分發(fā)和hash join操作的成本。不同的分布方式,布隆過(guò)濾的生成和使用略有不同:

  • 對(duì)于broadcast分發(fā)和replicate,每個(gè)PX進(jìn)程持有hash join左邊的完整數(shù)據(jù),對(duì)連接鍵生成一個(gè)完整的布隆過(guò)濾,掃描hash join右邊時(shí)使用。如果sql涉及多個(gè)維度表,維度表全部使用broadcast分發(fā),優(yōu)化器可能對(duì)不同的維度表數(shù)據(jù)生成多個(gè)的布隆過(guò)濾,在掃描事實(shí)表時(shí)同時(shí)使用。

  • 對(duì)于hash分發(fā),作為消費(fèi)者的PX進(jìn)程接收了hash join左邊的數(shù)據(jù)之后,每個(gè)PX進(jìn)程分別對(duì)各自的數(shù)據(jù)集生成布隆過(guò)濾,再?gòu)V播給作為生產(chǎn)者的每個(gè)PX進(jìn)程,在掃描hash join右邊時(shí)使用。

真實(shí)世界中,優(yōu)化器會(huì)根據(jù)統(tǒng)計(jì)信息和sql的過(guò)濾條件自動(dòng)選擇布隆過(guò)濾。通常使用布隆過(guò)濾使都會(huì)帶來(lái)性能的提升。某些極端的情況,使用布隆過(guò)濾反而造成性能下降,兩個(gè)場(chǎng)景:

  • 當(dāng)hash join左邊的數(shù)據(jù)集過(guò)大,比如幾百萬(wàn)行,而且連接鍵上的唯一值很多,優(yōu)化器依然選擇使用布隆過(guò)濾。生成的布隆過(guò)濾過(guò)大,無(wú)法在CPU cache中完整緩存。那么使用布隆過(guò)濾時(shí),對(duì)于hash join右邊的每一行記錄,都需要到內(nèi)存讀取布隆過(guò)濾做判斷,導(dǎo)致性能問(wèn)題。

  • 如果Join操作本身無(wú)法過(guò)濾數(shù)據(jù),使用布隆過(guò)濾時(shí)hash join右邊的數(shù)據(jù)都會(huì)命中。優(yōu)化器可能無(wú)法意識(shí)到j(luò)oin操作無(wú)法過(guò)濾數(shù)據(jù),依然選擇使用布隆布隆。如果hash join右邊數(shù)據(jù)集很大,布隆過(guò)濾可能會(huì)消耗明顯的額外cpu。

并行執(zhí)行計(jì)劃中典型的串行點(diǎn)

現(xiàn)實(shí)世界中,由于使用不當(dāng),并行操作無(wú)法并行,或者并行執(zhí)行計(jì)劃效率低下,沒(méi)有獲得期望的性能提升。本節(jié)舉幾個(gè)典型例子。

  1. 在sql中使用rownum,導(dǎo)致出現(xiàn)PX SEND 1 SLAVE操作,所有數(shù)據(jù)都需要分發(fā)到一個(gè)PX進(jìn),以給每一行記錄賦值一個(gè)唯一的rownum值,以及BUFFER SORT等阻塞操作。

  2. 使用用戶自定義的pl/sql函數(shù),函數(shù)沒(méi)有聲明為parallel_enable,導(dǎo)致使用這個(gè)函數(shù)的sql無(wú)法并行。

  3. 并行DML時(shí),沒(méi)有enable parallel dml,導(dǎo)致DML操作無(wú)法并行。

Rownum,導(dǎo)致并行執(zhí)行計(jì)劃效率低下

在’數(shù)據(jù)傾斜對(duì)不同分發(fā)方式的影響’小節(jié)中,我們新建一個(gè)表lineorder_skew把lineorder的lo_custkey列90%的值修改為-1。因?yàn)閘o_custkey是均勻分布的,我們可以通過(guò)對(duì)lo_custkey列求模,也可以通過(guò)對(duì)rownum求模,把90%的數(shù)據(jù)修改為-1。使用如下的case when語(yǔ)句:

1. case when mod(lo_orderkey, 10) > 0 then -1 else lo_orderkey end lo_orderkey

2. case when mod(rownum, 10) > 0 then -1 else lo_orderkey end lo_orderkey

通過(guò)以下的建表sql來(lái)測(cè)試兩種用法時(shí)的sql執(zhí)行性能,并行度為16。

create table lineorder_skew1 parallel 16 as select case when mod(lo_orderkey, 10) > 0 then -1 else lo_orderkey end lo_orderkey, lo_linenumber, lo_custkey, lo_partkey, lo_suppkey, lo_orderdate, lo_orderpriority, lo_shippriority, lo_quantity, lo_extendedprice, lo_ordtotalprice, lo_discount, lo_revenue, lo_supplycost, lo_tax, lo_commitdate, lo_shipmode, lo_status from lineorder;

不使用rownum時(shí),create table執(zhí)行時(shí)間為1分鐘,db time為15.1分鐘。QC只分配了一組 PX進(jìn)程,每個(gè)藍(lán)色的PX進(jìn)程以基于數(shù)據(jù)塊地址區(qū)間為單位,并行掃描lineorder表,收集統(tǒng)計(jì)信息,并加載到lineorder_skew1表。沒(méi)有數(shù)據(jù)需要分發(fā),每個(gè)PX進(jìn)程一直保持活躍,這是最有效率的執(zhí)行路徑。


大部分時(shí)間,AAS=16。


使用rownum時(shí),create table執(zhí)行時(shí)間為22.3分鐘,db time為38.4分鐘。SQL的執(zhí)行時(shí)間為使用lo_orderkey時(shí)的22倍。

執(zhí)行計(jì)劃中出現(xiàn)兩組PX進(jìn)程,PX SEND 1 SLAVE和BUFFER SORT兩個(gè)操作在之前的測(cè)試沒(méi)有出現(xiàn)過(guò)。根據(jù)跟隨table queue順序的原則,我們來(lái)閱讀這個(gè)執(zhí)行計(jì)劃:

  1. 藍(lán)色的PX進(jìn)程并行掃描lineorder,通過(guò)tablequeue0把所有數(shù)據(jù)分發(fā)給一個(gè)紅色的PX進(jìn)程 (第10~12行)。因?yàn)閞ownum是一個(gè)偽列,為了保證每一行記錄擁有一個(gè)唯一行號(hào),對(duì)所有數(shù)據(jù)的rownum賦值這個(gè)操作只能由一個(gè)進(jìn)程完成,為rownum列賦值成為整個(gè)并行執(zhí)行計(jì)劃的串行點(diǎn)。這就是出現(xiàn)PX SEND 1 SLAVE操作,性能急劇下降的原因。這個(gè)例子中,唯一活躍的紅色PX進(jìn)程為實(shí)例1 p008進(jìn)程。Lineorder的300M行記錄都需要發(fā)送到實(shí)例1 p008進(jìn)程進(jìn)行rownum賦值操作,再由這個(gè)進(jìn)程分發(fā)給16個(gè)藍(lán)色的PX進(jìn)程進(jìn)行數(shù)據(jù)并行插入操作。

  2. 實(shí)例1 p008進(jìn)程接收了16個(gè)藍(lán)色PX進(jìn)程分發(fā)的數(shù)據(jù),給rownum列賦值(第8行count操作)之后,需要通過(guò)tablequeue1把數(shù)據(jù)分發(fā)給藍(lán)色的PX進(jìn)程。但是因?yàn)橥ㄟ^(guò)tablequeue0的數(shù)據(jù)分發(fā)的還在進(jìn)行,所以執(zhí)行計(jì)劃插入一個(gè)阻塞點(diǎn)BUFFER SORT(第7行),把rownum賦值之后的數(shù)據(jù)緩存到臨時(shí)空間,大小為31GB。

  3. Tablequeue0的數(shù)據(jù)分發(fā)結(jié)束之后,實(shí)例1 p008把31GB數(shù)據(jù)從臨時(shí)空間讀出,通過(guò)tablequeue1分發(fā)給16個(gè)藍(lán)色的PX進(jìn)程進(jìn)行統(tǒng)計(jì)信息收集和插入操作


紅色的PX進(jìn)程只有實(shí)例1 p008是活躍的。消耗了16.7分鐘的db time。對(duì)于整個(gè)執(zhí)行計(jì)劃而言,兩次數(shù)據(jù)分發(fā)也消耗了大量的db cpu。通過(guò)Table queue 0把300M行記錄從16個(gè)藍(lán)色的PX進(jìn)程分發(fā)給1個(gè)紅色的 PX 進(jìn)程。通過(guò)Table queue 1把300M行記錄從1個(gè)紅色的PX進(jìn)程分發(fā)給16個(gè)藍(lán)色的PX進(jìn)程。


雖然DoP=16,實(shí)際AAS=1.5,意味著執(zhí)行計(jì)劃效率低下。


現(xiàn)實(shí)世界中,在應(yīng)用中應(yīng)該避免使用rownum。Rownum的生成操作會(huì)執(zhí)行計(jì)劃的串行點(diǎn),增加無(wú)謂的數(shù)據(jù)分發(fā)。對(duì)于使用rownum的sql,提升并行度往往不會(huì)改善性能,除了修改sql代碼,沒(méi)有其他方法。

自定義PL/SQL函數(shù)沒(méi)有設(shè)置parallel_enable,導(dǎo)致無(wú)法并行

Rownum會(huì)導(dǎo)致并行執(zhí)行計(jì)劃出現(xiàn)串行點(diǎn),而用戶自定義的pl/sql函數(shù),如果沒(méi)有聲明為parallel_enable,會(huì)導(dǎo)致sql只能串行執(zhí)行,即使用hint parallel指定sql并行執(zhí)行。我們來(lái)測(cè)試一下,創(chuàng)建package pk_test,包含函數(shù)f,返回和輸入?yún)?shù)一樣的值。函數(shù)的聲明中沒(méi)有parallel_enable,不支持并行執(zhí)行。

create or replace package pk_test authid current_user as function f(p_n number) return number; end; / create or replace package body pk_test as function f(p_n number) return number as l_n1 number; begin select 0 into l_n1 from dual; return p_n - l_n1; end; end; /

以下例子中在where語(yǔ)句中使用函數(shù)pk_test.f,如果在select列表中使用函數(shù)pk_test.f,也會(huì)導(dǎo)致執(zhí)行計(jì)劃變成串行執(zhí)行。

select /*+ monitor parallel(4) */ count(*) from customer where c_custkey = pk_test.f(c_custkey);

查詢執(zhí)行時(shí)間為54秒,db time也為54秒。雖然我們指定使用Dop=4并行執(zhí)行,執(zhí)行計(jì)劃實(shí)際是串行的。


在函數(shù)的聲明時(shí)設(shè)置parallel_enable,表明函數(shù)支持并行執(zhí)行,再次執(zhí)行sql。

create or replace package pk_test authid current_user as function f(p_n number) return number parallel_enable; end; / create or replace package body pk_test as function f(p_n number) return number parallel_enable as l_n1 number; begin select 0 into l_n1 from dual; return p_n - l_n1; end; end; /

此時(shí)查詢的執(zhí)行時(shí)間為12秒,db time為46.4秒。并行執(zhí)行如期發(fā)生,并行度為4。


除非有特殊的約束,創(chuàng)建自定義pl/sql函數(shù)時(shí),都應(yīng)該聲明為parallel_enable。pl/sql函數(shù)聲明時(shí)沒(méi)有設(shè)置parallel_enable導(dǎo)致無(wú)法并行是一個(gè)常見(jiàn)的問(wèn)題,我曾在多個(gè)客戶的系統(tǒng)中遇到。在11g中,這種情況發(fā)生時(shí),執(zhí)行計(jì)劃中可能會(huì)出現(xiàn)PX COORDINATOR FORCED SERIAL操作,這是一個(gè)明顯的提示; 或者你需要通過(guò)sql monitor報(bào)告定位這種問(wèn)題。僅僅通過(guò)dbms_xplan。display_cursor檢查執(zhí)行計(jì)劃是不夠的,這種情況執(zhí)行計(jì)劃的note部分,還是會(huì)顯示DoP=4。

并行DML,沒(méi)有enable parallel dml,導(dǎo)致DML操作無(wú)法并行。

這是ETL應(yīng)用中常見(jiàn)的問(wèn)題,沒(méi)有在session級(jí)別enable或者force parallel dml,導(dǎo)致dml操作無(wú)法并行。使用customer的1.5M行數(shù)據(jù)演示一下。

建一個(gè)空表customer_test:

create table customer_test as select * from customer where 1=0;

我們使用并行直接路徑插入的語(yǔ)句作為例子。分別執(zhí)行兩次insert,第一次沒(méi)有enable parallel dml,insert語(yǔ)句如下:

insert /*+ append parallel(4) */ into customer_test select * from customer;

Insert語(yǔ)句執(zhí)行時(shí)間9秒。雖然整個(gè)語(yǔ)句的并行度為4,但是執(zhí)行計(jì)劃中,第2行直接路徑插入操作LOAD AS SELECT是串行執(zhí)行的。


此時(shí)執(zhí)行計(jì)劃的Note部分會(huì)顯示PDML沒(méi)有啟用:

Note ----- - PDML is disabled in current session

啟用parallel dml之后,重新執(zhí)行insert語(yǔ)句。

alter session enable parallel dml;

此時(shí)insert語(yǔ)句執(zhí)行時(shí)間為3秒,執(zhí)行計(jì)劃中第三行,LOAD AS SELECT操作是可以并行的。

到此,關(guān)于“怎么理解Oracle的并行執(zhí)行”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI