https://dbaplus.cn/news-10-374-1.html
Oracle企業版有一項非常厲害的技術:並行查詢,也就是說一個語句可以雇佣多個服務器進程(parallel slaves也叫PX slaves)來完成這一個查詢所需要的結果。
並行操作不僅僅能夠充分利用主機的CPU資源,也能夠充分利用系統的IO資源、內存資源,這看起來是一個優點,但是也需要看情況,如果數據庫系統沒有太多的空閑CPU、空閑IO或空閑內存資源,那么並行技術是否要使用非常值得考慮,甚至即使系統有着很多的CPU空閑資源,但是IO資源已經遠遠不夠,那么同樣需要考慮是否要使用並行(並行往往產生大量的IO)。
鑒於並行操作的工作方式,不能讓它在系統中被濫用,否則可能導致系統的資源很快的被耗盡。並行操作本身也是復雜的,它有着很多串行執行所不具備的概念,例如table queue,數據分發方式等等,並且閱讀並行語句執行計划的方式也與串行可能會有所不同。
並行操作的目的是為了提升語句執行的線性度,如果一個語句串行執行的時間為4分鍾,那么通過指定4個並行來操作,可以加快查詢執行時間為1分鍾,當然這只是一種預期,現實的情況往往不能達到這種線性度。有一些消耗和事實需要了解:
-
雇佣並行進程本身需要一些時間,這些時間往往比較短,如果進程池中沒有可用的並行進程,那么還需要操作系統去spawn出需要的並行進程,這時數據庫可能會遭遇os thread startup等待。如果語句執行時間只有數秒,你要考慮它是否適合使用並行。
-
QC進程給PX slaves分配工作,這會消耗一些時間,這個時間一般也非常短。例如QC進程需要給每個PX slave進程分配掃描ROWID的范圍。
-
如果並行查詢要返回大量的數據給客戶端,那么僅有的一個QC進程本身可能會成為瓶頸。
-
由於Oracle的並行執行采用的是生產者消費者模型,因此一般DOP為4的查詢,最終雇佣的PX slaves為8,再加上QC進程本身,一共會占用9個系統進程,你要認識到付出的這些是否值得。
-
在Exadata下即使使用串行查詢,由於在IO層面默認就是並行,因此Exadata下的語句並行效果沒有非Exadata下好。
為了讓並行能夠非常好的發揮作用,有一些要求需要被滿足:
-
非常有效率的執行計划,如果執行計划本身非常糟糕,使用並行可能並不能多大程度上改善語句的執行效率。
-
數據庫系統有着充足的資源可用。這點已經在文章的開頭提到過。
-
工作量的分配沒有明顯的傾斜,大家都熟悉短板理論,如果某一個PX slave干了絕大部分的活,那么最終的響應時間最大的瓶頸就是它。
也許上面的很多概念和術語你還不清楚,沒關系,我們下面的內容都會介紹到。使用並行首先應該考慮的問題是如何分配工作量,在串行執行的情況,這個問題不用考慮,只有一個進程干活,所有的工作都是由它來完成,但是如果使用了並行操作,那就意味着有多個進程在干同樣一件事,工作的分配就顯得非常的重要。
(一)單表的並行操作
對於單表的並行操作,工作量的切分是比較簡單的,Oracle也沒有設計任何復雜的算法,它一般是按照ROWID或者分區(假如它是分區表的話)來分配工作。例如下面的並行查詢:

上面的SQL及其執行計划顯示,對表test以並行度2進行了記錄數的統計,Id為5的行源Operation部分為:PX BLOCK ITERATOR,這是一個在並行操作中經常能看到的一個操作,代表了QC進程按照ROWID把表做了切分,每個PX slave掃描表的不同范圍,然后每個PX slave聚合出自己所掃描部分的記錄數(Id=4,SORT AGGREGATE ),最后把結果發送給QC,QC進一步聚合這些PX slaves的結果形成一個記錄返回給客戶端。
通過SQL MONITORING可以看到的更為直觀(下圖),絕大部分的工作都是通過藍色的PX slaves來完成的,然后這些PX slaves把各自做過預聚集的結果發送給(行源ID為3)QC做最終的聚合。

不過我們隨着后續的學習會發現,這里的這個例子只雇佣了一組PX slaves進程,這在Oracle並行的世界中是一個特殊案例。按照Oracle的生產者、消費者模型,一般會雇佣兩組PX slaves,一組作為生產者掃描數據,另一組作為消費者把從生產者接收過來的數據做各種加工。(不過這個例子可以把QC作為消費者看待)。
本文大量使用了SQL MOMITORING工具,如果你對這個工具還不熟悉,請參閱我的另一篇文章: http://www.jianshu.com/p/ce85dd0c05ab
我們對SQL進行簡單的改造,增加ORDER BY部分,看看結果會怎么樣。


同樣我們通過SQL MONITORING來進行可視化解析,【操作】列出現了兩種不同顏色的PX slaves,紅色的PX slaves作為生產者正在掃描表HASH_T1,然后把掃描到的數據分發給藍色的PX slaves消費者,PX slaves消費者接收到這些數據后並做排序然后把結果集發送給QC。 這個例子雖小,但是五臟俱全,在Oracle並行執行中,一個可以並行的操作單元(樹)稱為Data Flow Operator,一個QC代表了一個DFO單元,一個查詢可以有多個DFO單元(DFO tree),例如典型的像union all語句,就可以有多個DFO單元,不同的DFO單元之間也可以並行。
具備了Oracle並行執行生產者和消費者的概念,繼續看上圖中的【名稱】列,會發現有TQ10001,TQ10000的東西,這個是啥?
(二)Table Queue
上面已經提到Oracle並行操作有生產者和消費者的概念,生產者和消費者分別代表着一組進程,他們之間需要傳遞消息和數據,那么他們是靠什么來進行傳遞消息和數據的呢?這就是table queue的作用。 繼續以上圖為例:

這里一共包含了兩組PX slaves,一組為紅色的生產者,一組為藍色的消費者,生產者通過ID為6,7的行源掃描表HASH_T1,同時通過ID為5的行源把掃描結果寫入table queue TQ10000(PX SEND RANGE),消費者從table queue TQ10000讀取數據然后做排序(PX RECEIVE),消費者對於已經完成排序的結果通過table queue TQ10001發送給QC進程,QC進程把接收到的結果聚合后發送給客戶端。
(三)如何切分多表
為什么要引入數據分布算法
對於單表(無JOIN)的數據切分是非常簡單的,只需要按照ROWID做切分就可以保證結果的正確,因為多個並行slaves之間沒有數據的交叉,也就不會有數據的丟失,而且按照ROWID切分也非常容易保證每個PX slave的工作量均勻。但是如果是兩表的JOIN呢?你如何保證1/N的X表的記錄和相對應的1/N的Y表的記錄在一個並行操作內(也就是由一個並行進程處理)?兩個表都按照ROWID來切分是不能保證的。 為了讓例子足夠的簡單,可以通過如下例子來進行描述:
集合一: 【1,3,5,7,9,11】
集合二: 【1,9,3,6,7,8,5】
假如要求使用並行度2來判斷,【集合二】和【集合一】有多少數據有交集,該如何實現? 我們模擬通過ROWID來切分,把【集合一】按照順序切分為2部分:
set 1:1,3,5 =>進程1
set 2:7,9,11=>進程2
我們再使用同樣的辦法,把【集合二】切分為2部分:
set 3:1,9,3 =>進程1
set 4:6,7,8,5 =>進程2
通過上面一系列的操作我們把2個集合都切分為了2份,然后我們通過進程1對set 1與set 3做join,進程2對set 2與set 4做join,OK? 顯然是不行的,因為最終的結果集是不對的。
兩個集合做JOIN正確的結果是:3,5,7,9
但是按照上面的算法,set 1和set 3的結果集為3,set 2和set 4的結果集為7,最終的結果集為3,7,丟失了5,9兩個結果。
因此不能為了加快查詢的速度而不保證結果正確性對對數據進行隨意切割。那么Oracle是如何做的?如何保證進程讀取了X表的1/N的數據與Y表相對應的1/N數據?
從這里看出了引入了數據分布算法的重要性,也解釋了為什么運行並行度N需要2N個並行slave來完成工作,一組進程用來掃描表X,然后把數據按照分布算法把數據分發給另一組進程Y,這樣表X的數據分布完成后,Y的表記錄要根據X表的分布算法來決定自己的分布方式。你看到這里可能有些地方可能還看不明白,沒關系,后續有足夠的內容讓你明白這些操作。
BroadCast
繼續前面的例子:
【集合一】:1,3,5,7,9,11
【集合二】:1,9,3,6,7,8,5
broadcast的分發方式為(這里假設並行度為2):
Oracle首先需要產生2組PX slaves,一組為生產者包含2個PX slave進程,一組為消費者,同樣包含2個PX slave進程,(注意生產者和消費者角色是可能互換的)。
每個生產者PX slave按照ROWID切分,掃描1/2的【集合一】,然后廣播給每一個消費者的PX slave,最終每一個消費者的PX slave都有一份全量的【集合一】。
然后每個消費者的PX slave進程按照ROWID切分,掃描【集合二】,然后與【集合一】做關聯判斷,最終得出結果集。
這里的關鍵是,每個消費者的PX slave都持有了全量的【集合一】,因此不需要再對集合二有任何的分發需要,只需要按照ROWID掃描然后再進行JOIN操作就能夠保證結果的正確性。
集合一:【1,3,5,7,9,11】
分發后為:
set1: 1,3,5,7,9,11 =>進程1
set2:1,3,5,7,9,11 =>進程2
集合二:【1,9,3,6,7,8,5】
分發后為:
set 3: 1,9,3,6 =>進程1
set 4: 7,8,5 =>進程2
set 1,set 3的結果集為1,3,9,set 2和set 4的結果集為5,最終的結果集為1,3,5,9,這樣不但把工作量做了比較均勻的切分,而且保證了結果的正確性。
這里我們通過一個具體的查詢例子再來看一下整個過程:

表T1的數據量為70,表T4的數據量為343000。


我們通過添加hint pq_distribute(t1 none broadcast)強制讓hash join左邊的表進行了廣播分發,根據SQL MONITORING的輸出,我們做如下分析:
-
(行ID 9,8,7),生產者紅色進程(【操作】列)按照ROWID做切分掃描表T1,然后把掃描的結果寫入table queue,以廣播方式做分發,ID為7的行源PX SEND BROADCAST操作代表了廣播的分發方式。
-
行ID6,5,藍色的消費進程(【操作】列)接收到紅色PX slave廣播的數據,然后構建HASH TABLE。每一個藍色的消費PX slave都接收到了全量的T1表 的數據,根據【實際行數】列可以顯示這一點,表T1總共70行的數據經過廣播分發后,實際產生了70*2(並行度)=140行的記錄。
-
行ID 11,10,藍色的消費進程按照ROWID切割掃描T4表並與前面構建的HASH TABLE做JOIN。
這里並沒有對T4進行任何的分發,認識到這一點很重要,藍色的消費進程只需要按照ROWID范圍掃描即可,因為T1表的數據在每個消費者的PX slave都保持着全量。
這里我們做一個階段性的總結:
對於broadcast分發方式來說:
-
HASH JOIN右邊的表不用分發。
-
BROADCAST方式, 沒有結果不對的風險,因為消費者的每個PX slave持有了全部的HASH JOIN左邊表的數據,每個消費者進程都持有一個完整的HASH TABLE。
-
HASH JOIN左邊表 如果小的話,分發代價不大。但是隨着並行度DOP的提高或者左邊表數據量的增大,分發的代價會越來越大。
-
如果左邊表小的話,BROADCAST的執行計划具有非常好的擴展性。
-
第一組PX進程掃描HASH JOIN左邊表廣播給第二組PX slave,CPU,內存,競爭都會有消耗,競爭的消耗來自於第一個組的每一個進程掃描的數據都要廣播給第二組的每一個進程,如下圖:

Replicate
replicate代表每個並行進程都全量掃描hash join左邊的表,不按照rowid做區分,由於數據是被每一個進程全量掃描的,因此不需要再對數據做分發,也就只需要一組PX slaves。

觀察操作列只有一組藍色的PX slaves進程,這里沒有涉及到數據的分發:
-
2個進程全量掃描reptest表,然后構建hash table(全量的hash table)
-
掃描完成后,2個進程按照ROWID范圍掃描hash_t1表,由於2個進程持有了全量的reptest表的hash table,因此對於hash_t1表不需要分發。掃描hash_t1表過程中探測hash table。
Hash分發
就像上面提到的,broadcast/replicate分發方式有一個問題是,因為消費者的每一個PX slaves要持有完整左邊表的記錄, 因此適合左邊表比較小的情況。如果對於兩個大表的HASH 連接,Oracle一般使用HASH的分發方式。例如還是上面的例子:
【集合一】:1,3,5,7,9,11
【集合二】:1,9,3,6,7,8,5
【集合一】和【集合2】按照同樣的HASH 函數分發后,總能保證有關聯的數據對在一起,這樣就能保證結果集的正確性。但是這樣的方式,多出了一個代價,那就是對於【集合二】也需要做HASH分發,會多出一些CPU資源的消耗,相對於廣播的分發方式,只有【集合一】需要做分發。
我們來看一個具體的例子:


-
首先紅色的生產者PX slaves按照ROWID切分並行掃描表HASH_T1,然后依據HASH算法把記錄通過table queue TQ10000分發給特定的藍色的消費者PX slave。
-
藍色的消費者從table queue TQ10000接收到數據后構建HASH TABLE。
-
上面2步操作完成后,紅色的生產者PX slaves繼續按照ROWID切分並行掃描表HASH_T2,然后按照HASH算法把記錄通過table queue TQ10001分發給特定的藍色消費者PX slave,藍色消費者PX slave從table queue TQ10001接收數據並與前面構建的HASH TABLE做JOIN。最后每個藍色的消費者PX slave把自己聚合的結果通過table queue TQ10002發送給QC。
注意:
-
【實際行數】列,記錄按照HASH分發后並沒有增加。
-
對hash_t2掃描過程,由於數據需要分發,因此會有同時2組PX slaves同時活躍。
HASH分發有着很好的擴展性,每個進程有部分的HASH 表,而不是完整的HASH表,每一行只會分發給一個特定的PX SLAVE。而不是像broadcast分發把每一行廣播給每一個SLAVE。
但是就像上面已經指出過,待對HASH JOIN左邊表分發完畢后,同樣對於HASH JOIN右邊的表也需要進行分發,多了一次分發的代價,增加了一些CPU和內存的成本。
HYBRID-HASH
錯誤的分發方式可能會對並行執行帶來非常大的性能問題,Oracle 12C介紹自適應的並行分發方法,hybrid hash,在真正執行過程中,再決定該使用何種分發方式,Oracle 優化器要做到這一點,使用了statistics collector,它在語句運行過中統計語句的一些運行時信息,例如返回記錄的數量等等。需要注意使用了HYBRID-HASH后,每次語句執行,都要通過statistics collector來動態決定使用的並行分發方式。

例如上面的執行計划,觀察行源ID 7,並行執行過程中會統計結果集的返回值數量,如果返回的結果集數量小於並行度*2,那么會使用廣播方式來進行數據分發,反之則使用HASH的數據分發方式,作為回應,在行源ID 為6的分發方式確定后,行源ID 11再決定使用round-robin還是hash分發。
並行執行計划的閱讀順序
我剛做DBA那會,一些老DBA告訴我如何看並行執行計划,那就是把PX相關的操作都統統抹去,然后再看,例如:

我們把相關的PX等操作都全部去掉,最終和串行執行的如下文本是"等價"的:

真的等價嗎? 在串行執行過程中,對於上面執行計划的執行順序是這樣的:
掃描T3表,構建hash table,掃描T2表,構建hash table,掃描T1表構建hash table,最后掃描T4表,掃描到的每一個記錄都要探測前面所產生的3個hash table。
但是並行執行的執行順序並不一定是按照上面描述的順序,對於並行執行計划的閱讀要跟隨table queue的創建順序,它代表着並行執行中數據分發的順序。因此就上面的並行執行,執行順序為:
-
掃描table T1,構建hash table,之所以首先掃描T1,是因為table queue的編號TQ10000 是最小的。
-
根據table queue TQ10001的位置知道,然后掃描table T4並與上面的hash table做hash join。
-
根據table queue TQ10002的位置知道,接着掃描table T2,構建hash table,然后,上面兩步產生的結果集與這個hash table做hash join。
-
根據table queue TQ10002的位置知道,最后掃描table T3,構建hash table,然后上面三步產生的結果集與這個hash table做hash join。
V$PQ_TQSTAT
v$pq_tqstat視圖是非常特別的,它的內容只記錄在QC進程的私有PGA中,而且只在 並行查詢結束后內容才會被填充,因此如果並行執行過程中,你取消了查詢,那么查詢這個視圖依然不會有任何結果,因為它只存在進程的PGA中,因此你不能通過另一個會話去查詢它。 可以通過它了解並行執行過程中數據是如何通過table queue分發的。舉兩個例子:
HASH_T1數據量9999999

例如我們對hash_t1 以並行度4進行記錄統計,執行完成后,查看v$pq_tqstat視圖:

4個生產者把各自掃描到的記錄做了匯聚各自產生一個記錄並把它寫入table queue,QC通過table queue接收了這四個記錄。注意NUM_ROWS代表的是PX slaves通過table queue寫入、讀取的數據量,可以通過NUM_ROWS的值非常容易看出並行進程的工作量是否均勻,是否有並行傾斜存在。
再看一個復雜點的例子:

觀察SQL的hint部分,強制讓hash join的左邊表使用了broadcast的分發方式,結合上面的輸出和下面的圖一起看,每個紅色的生產者先按照ROWID范圍掃描HASH_T2,然后把數據通過table queue TQ10000廣播給藍色的消費者,由於采用了並行度4,因此其實每個生產者真正寫入table queue中的數據量是掃描數據量的4倍(9999999 是真實的記錄數,經過廣播分發產生了9999999 *4 的記錄數),消費者從table queueTQ10000中接收數據后構建hash table,每個消費者PX slave都構建了表hash_t2完整的的hash table,然后藍色消費者開始掃描hash_t1,並與之前構建的hash table做join,最后每個藍色消費者把各自最終形成的預聚合結果發送給QC(這里其實已經轉化了角色變為了生產者),QC接收到4條記錄。

消費者生產者模型的限制
根據上面的介紹,你已經知道了,一個並行操作內一般會具有兩組PX slave進程,一組為生產者,一組為消費者。生產者通過table queue發送數據,消費者通過table queue接收數據。而且對於消費者和生產者模型,有一個很大的限制是:一組DFO單元最多只能有兩組PX slave進程,之所以有這個限制,一方面可能是Oracle公司為了保持並行代碼的簡潔性,一方面由於每個PX slave進程之間和每個PX slave與QC之間都要維持一個通信通道(table queue)用於傳遞消息和數據,如果允許的PX slave有太多組,可能會導致通信通道指數級增長。
例如一個DOP為5的並行操作,PX slave之間需要的通道數為55,PX slave與QC之間的通道數為25,共需要(5+2)5=35個通道。可想而知,如果Oracle允許一個並行操作內有3組PX slave,需要維持的連接數有多少,我們假設當前服務器共運行了50個並行,那么三組PX slave進程產生的通道數為5050*50=125000個,還不包括PX slave與QC之間的通道,嚇尿了不?
parallel_execution_message_size
如果進程之間傳遞消息的通道數多但不占用數據庫資源可能也並不是什么大的問題,但是事實不是這樣的,進程之間傳遞消息的通道的內存占用大小是由參數parallel_execution_message_size控制的,在11GR2版本這個參數的值為16K,在以前的各個版本這個參數的值可能大小並不一樣(每個版本都有增大的趨勢),在非RAC環境下,每個通道的大小最大可以為3parallel_execution_message_size,在RAC環境下,每個通道的大小最大可以為4parallel_execution_message_size。
例如一個DOP為20的查詢,非rac環境下通道所占用的內存最大可能為: PX進程的通道內存+QC、PX進程之間的通道內存=202016K3+22016K3=21120K,接近21M的內存。 通道的內存默認是在large pool中分配,如果沒有配置large pool則在shared pool中分配。
計算通道內存的公式:
單節點
(NN+2N)316k
RAC節點
(NN+2N)416k
Hash Join Buffered
其實不是hash join的缺陷。 我們已經介紹過生產者消費者模型,它有一個很大“缺陷”是,一個並行操作內,最多只能有2組PX slave,2組PX slave通過table queue來傳遞消息和交互數據,因此在一組SLAVE在讀table queue的時候,不能同時去寫另一個table queue。是不是不太好理解? 我們通過一個例子來進行描述:


這個例子里我通過hint pq_distribute(b hash hash)強行讓數據以hash方式進行分發,關注行源ID為3的操作,出現很陌生的一個操作:hash join buffered,不要被這個buffered名字所迷惑,它代表着數據暫時不能向上流動,必須先暫時存放在這里,語句的執行順序是這樣的:
-
首先紅色的生產者PX slave掃描hash_t3表,並對掃描的記錄按照HASH分發方式把相關記錄寫入table queue TQ10000
-
藍色的消費者PX slave從table queue TQ10000接收數據並構建hash table。
-
上面操作結束后,紅色的生產者繼續掃描hash_t1表,並對掃描的記錄按照HASH分發方式寫入table queue TQ10001
-
藍色的消費者PX slave從table queue TQ10001接收數據,並與上面的HASH TABLE做探測,但是結果並不能寫入table queue TQ10002,而是先暫時緩存起來(hash join buffered的由來)
-
等HASH分發完成之后(也就是這兩組PX slave不活躍以后),然后由一組PX slave把結果集通過table queue TQ10002發送給QC。
為什么要這樣?貌似是沒有道理的。
這就是因為hash分發要求對hash join的右邊也要進行分發,分發操作涉及了2組PX slave進程,一組負責掃描,一組負責接收數據,也就是一組PX slave把掃描的數據寫入table queue,一組負責從table queue讀取數據,這個時候不能 再進行數據的分發操作,因為join的結果集不能寫入另一個table queue TQ10002。
如果結果集較大的話,這個可能在一定程度上會導致消耗很多臨時表空間,導致大量的磁盤讀寫IO,進而引起性能降低。
如果確實產生了這種情況,可以通過改用broadcast分發來避免出現這種情況,因為broadcast分發對於hash join的右邊並不需要進行分發


例如改成broadcast后,hash join buffered操作已經消失了。
布隆過濾
我們有必要介紹一下布隆過濾,它在11GR2之后版本的並行里有非常大的作用。bloom filter並非Oracle的發明,bloom filter技術出現的時候Oracle軟件還未誕生,它在1970年由Burton H.Bloom開發出來,布隆過濾到什么?
布隆過濾或者說布隆過濾器,是一種數據結構,它能夠快速的判斷一個數據是否屬於一個集合,hash join本身是非常消耗資源的,也是非常慢的,布隆過濾比hash join快很多。
關於布隆過濾的詳細介紹請參照:http://www.cnblogs.com/haippy/archive/2012/07/13/2590351.html

布隆過濾器基於一個有M位的數組,例如上圖數組的大小有18位,初始化的時候全部的值都為0,如果要理解布隆過濾是如何工作的,必須要知道在什么情況下,這些標志位需要置為1,上圖中{X,Y,Z}代表着一個集合,這個集合有3個值(元素),仔細觀察每一個值都延伸出了三個線,在這里代表着每一個值都經過3個HASH函數計算,計算出來的值的范圍是從0-17(數組的長度),例如,X經過3次HASH函數計算,值分別為:1,3,13,然后對應的標志位被置為1,Y,Z同理把相應的標志位置為1。經過一番HASH計算,{X,Y,Z}集合的所有元素都已經經過了HASH計算,對應的標志位也都置為了1,然后我們再探測另一個集合,這里另一個集合的元素為W,W同樣需要經過相同的3個HASH函數計算,並且檢測對應的位置是否為1,如果對應的位置都為一,那么W可能(僅僅是可能)屬於這個集合,如果有任何的位置不為1,那么這個W一定不屬於這個集合。由於布隆過濾並不對值進行精確的匹配(而HASH JOIN是需要精確匹配的),因此可能會有一些不該屬於集合的值穿越了布隆過濾器。
布隆過濾器有如下特點:
-
構建布隆過濾數組要求的內存非常小,經常可以完全放入在CPU的cache中。當然布隆過濾的數組越大,布隆過濾誤判的可能性也就越小。
-
由於不需要精確匹配,因此布隆過濾的速度非常的快,但是有一些不該出現的值可能會穿越布隆過濾器。

PX Deq Credit: send blkd與PX Deq Credit: need buffer
布隆過濾有啥用呢?也許你是一位有着豐富經驗的老DBA,那么你對PX Deq Credit: send blkd 、PX Deq Credit: need buffer等待事件也許就比較熟悉。
經過上面的介紹,我們已經具備了很多的知識,table queue,生產者消費者模型等等,一組消費者PX slave寫入table queue,另一組通過讀取table queue來獲取數據,完成進程間數據的傳遞,但是一定會出現一種情況,當一組生產者PX slave在往table queue中寫入數據的時候,發現table que中的內存已經滿了,沒有剩余內存可以寫了,這種情況大部分時候都意味着消費者PX slave從table queue中消費數據過慢,過慢最大可能原因是由於消費者不得不把table queue中讀取到的數據溢出到磁盤,從內存讀取數據寫入磁盤是個很慢的操作,因此在這種情況下,就會遭遇PX Deq Credit: send blkd 、PX Deq Credit: need buffer等待,如何優化?這種情況下,布隆過濾就發揮了作用。
如果優化器認為表X返回1000條記錄,表Y需要掃描一億條記錄,但是經過HASH JOIN后,有90%都不需要返回,這種情況下使用布隆過濾在進行HASH分發前預HASH JON。這樣經過布隆過濾器,有大量的記錄就被布隆過濾器所淘汰,最后HASH JOIN右邊的結果集就變得非常小,也就讓HASH 分發的數據量變得非常的小,大大減少了出現PX Deq Credit: send blkd 、PX Deq Credit: need buffer的概率。如果不使用布隆過濾,進程不得不傳遞大量的數據給另一組進程,增加了內存,CPU,增加了兩組進程的進程間競爭。
不要期待布隆過濾是完美的,他能消除掉大部分的行,但是不是 所有的行,因此有一些不需要的數據會穿過布隆過濾器達到第二組進程。
並行度降級
無論你使用的是手工指定DOP,還是使用11G的AUTO DOP,運行時的DOP都有可能與你預期的不一樣:可能被降級。可能會有很多種原因導致並行被降低,例如,當前系統中可用的並行進程已經不能滿足需要的DOP,或者你已經使用了Oracle的資源管理器對並行度做了限制,等等。
監控並行度降低的最好工具是oracle 12.1版本的SQL MONITORING,例如:

如上圖,在【一般信息】部分,將你的鼠標放在Execution Plan部分的藍色小人上,將會出現一些並行度的信息,例如上圖中,運行時間的DOP為4,實際請求的並行服務進程為10,實際分配的並行服務進程為4,並行度被降低的百分比為60%。
為了找出語句被降級的理由,你可以點擊【計划統計信息】部分,PX COORDINATOR行源的其他列,如下圖,用紅色框標記:

點擊后出現:

以下是被降級的一些代碼說明:

我這里的情況是,由於系統可以使用的並行進程不足導致分配並行資源失敗。 如果你不方便使用EMCC,也可以通過視圖觀察到並行度降級的情況,但是被降級的理由,暫時還沒有視圖反應(或者我還不知道,如果你知道請告訴我)

DEGREE 列為實際的並行度,REQ_DEGREE 為請求的並行度。 有一些手段可以避免並行度降級,例如如果使用的是ORACLE 11G版本,可以使用自動並行管理功能,然后結合在語句級指定並行度。因為自動並行度功能一單被打開,並行語句排隊功能將被啟用,如果語句運行時發現沒有足夠的可用並行進程,那么會排隊等待,直到有滿足目標的可用並行進程。
多個DFO單元
一些命令可以有多個DFO單元,因為每個DFO單元最多可以使用2個PX slaves set,如果一個命令有多個DFO單元,那么它就可以使用超過2個PX slaves set,可以在執行計划里看到是否使用了多個DFO單元:


行ID為6和12的行源兩處都有coordinator標識,這意味着這個命令使用了2個DFO單元。

通過SQL MONITORING也可以看到這個命令具有了2個並行組,理論上每個DFO單元之間可以同時進行並行操作,但是我們這個例子里,兩個DFO單元之間的執行順序是,先執行DFO單元1,再執行DFO單元2,可以通過【時間表】列看到,第一個DFO單元先活躍,等結束后,第二個DFO單元開始活躍。
從上圖還可以看出,DFO單元2復用了DFO單元1的並行進程,沒有重新產生新的並行進程,從並行進程編號上可以看出這一點。SQL MONTIRONG是不是超級好用?
vpq_sesstat視圖,可以知道語句運行時的DFO單元的數量(DFO Trees),並行集的個數(Slave Sets ),服務進程的個數(Server Threads),執行所采用的並行度(DOP)。如下:

