Oracle並行基礎二

bitifi發表於2016-07-05

Oracle並行基礎(連載二)

作者:沃趣科技高階資料庫技術專家 魏興華



消費者生產者模型的限制

根據上面的介紹,你已經知道了,一個並行操作內一般會具有兩組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。是不是不太好理解?
我們透過一個例子來進行描述:

select /*+ parallel(6) pq_distribute(b hash hash)*/ * from hash_t3 a ,hash_t1 b where a.id=b.id;


  這個例子裡我透過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的右邊並不需要進行分發

select /*+ parallel(6) pq_distribute(b broadcast none)*/ * from hash_t3 a ,hash_t1 b where a.id=b.id;

例如改成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行源的其他列,如下圖,用紅色框標記:
點選後出現:

以下是被降級的一些程式碼說明:
350 DOP downgrade due to adaptive DOP
351 DOP downgrade due to resource manager max DOP
352 DOP downgrade due to insufficient number of processes
353 DOP downgrade because slaves failed to join
我這裡的情況是,由於系統可以使用的並行程式不足導致分配並行資源失敗。
如果你不方便使用EMCC,也可以透過檢視觀察到並行度降級的情況,但是被降級的理由,暫時還沒有檢視反應(或者我還不知道,如果你知道請告訴我)

DEGREE 列為實際的並行度,REQ_DEGREE 為請求的並行度。
有一些手段可以避免並行度降級,例如如果使用的是ORACLE 11G版本,可以使用自動並行管理功能,然後結合在語句級指定並行度。因為自動並行度功能一單被開啟,並行語句排隊功能將被啟用,如果語句執行時發現沒有足夠的可用並行程式,那麼會排隊等待,直到有滿足目標的可用並行程式。

多個DFO 單元

一些命令可以有多個DFO單元,因為每個DFO單元最多可以使用2個PX slaves set,如果一個命令有多個DFO單元,那麼它就可以使用超過2個PX slaves set,可以在執行計劃裡看到是否使用了多個DFO單元:
select count() from (select /+ parallel(a 4) /count() from hash_t1 a
union
select /+ parallel(b 4) / count(*) from hash_t2 b)


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

透過SQL MONITORING也可以看到這個命令具有了2個並行組,理論上每個DFO單元之間可以同時進行並行操作,但是我們這個例子裡,兩個DFO單元之間的執行順序是,先執行DFO單元1,再執行DFO單元2,可以透過【時間表】列看到,第一個DFO單元先活躍,等結束後,第二個DFO單元開始活躍。
從上圖還可以看出,DFO單元2複用了DFO單元1的並行程式,沒有重新產生新的並行程式,從並行程式編號上可以看出這一點。SQL MONTIRONG是不是超級好用?

v$pq_sesstat檢視


透過查詢v$pq_sesstat檢視,可以知道語句執行時的DFO單元的數量(DFO Trees),並行集的個數(Slave Sets ),服務程式的個數(Server Threads),執行所採用的並行度(DOP)。如下:

寫到這裡文章已經有點長了,對於12C的新特性還少有涉及,對於並行執行傾斜的內容也還未涉及,對於布隆過濾的傳遞和高階知識也未涉及。對於這些內容,我會在下一篇進行介紹。


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

相關文章