(轉載)處理SQL解析失敗導致share pool 的爭用
通過關聯x$kglcursorx$kglcursor_child_sqlid視圖;
通過使用Oracle10035Event事件可以找到解析失敗的SQL;
通過oraclesystemdump也可以找到解析失敗SQL;
以下我們來看看這個精彩的案例分享。
背景介紹客戶的一套重要生產系統,出現了性能問題。這個問題涉及的信息如下:
月底時候數據庫主機的CPU利用率長期在100%左右。
數據庫中出現大量的latch:librarycache競爭
系統概況
該系統為OLAPOLTP混合系統,平時為交易型數據庫。每個網點實時數據上傳,月底會有統計類報表產生。
以下為數據庫負載曲線,可以看到在月底復雜急劇上升,導致業務不能正常操作。
以下為故障時間點部分AWR截圖。
從LOADPROFILE看當前數據庫每秒有158次的硬解析,總的解析在1082次。
這個時間點的TOP5等待事件中latch:librarycache與kksfbcchildcompletion排在前列,librarycachelatch占到將近有70%。
Latch:Oracle用於控制內存並發的串行鎖機制
共享池latch競爭一般導致的原因有以下集中:
literalSQL所謂的literalSQL就是沒用使用綁定變量值的SQL比如select*fromenmowhereid=100;
硬解析比如一個新執行的SQL沒有在共享池中,那麽就要經歷一個硬解析的過程,關於過程這裏就不在多講
SQL不能共享,不能共享的原因有很多比如沒有在同一個用戶下面執行
SQLVERSION大量高版本SQL也會導致共享池的競爭
另外就是主機出現大量換頁,比如在AIX環境下大量計算內存使用了SWAP會導致類似的問題
還有就是查詢一些底層的視圖比如x$ksmsp在某些版本下高並發的系統中直接查詢這些視圖會出現大量的latch競爭
還有就是SGA大量抖動或者模擬調整的時候也會導致此問題
Oracle各個版本上也存在相關的BUG會導致
根據以上幾點我們去分析到底此問題出現在什麽地方。
首先數據庫等待事件除了librarycachelatch之後就是kksfbc
K[Kernel]K[Kompile]S[Shared]F[Find]B[Best]C[Child]
該函數用以在軟解析時找尋合適的子遊標,是否該故障是由於大量VERSIONCOUNT引起呢?
從這個時間點AWR來看沒有看到大量versioncount的SQL出現。
分析latch的時候AWR有一個非常重要的數據。
從LatchMissSource的數據可以看到,絕大多數都是對於sharedpoollatch的sleeps,
從AWRSleep來看sharedpool排在了第一位。從調用的函數來看都是發生在硬解析這個過程中。
以下為一些常見函數的功能:
Kghfrunp:KGH:Askclienttofreeunpinnedspace
Kghdmp:x$ksmspisafixedtablebasedonkghmetadata.Thenumberoflatchsleepsfor"kghdmp"willincreaseifx$ksmspifaninstallationselectsfromthisfixedtabletoofrequently.
kghupr1:un-pinrecreatable
kghaloKGH:mainallocationentrypoint
kghgexKGH:Getanewextent
kghalfKGH:Non-recoverablyallocateafreeablechunkofmemory
有很多函數這裏就不一一列舉。
當前現在也可以排除人為查詢底層視圖導致的latch競爭因為沒有看到相關函數出現,插播一個類似的案例。
像這種情況很明顯就是有人查詢了底層的視圖導致的sharedpool競爭。
從主機最早的信息來看也是沒有SWAP競爭出現的。
SGA沒有大量的resize也可以排除掉由於SGA組件抖動引起的。
從以上信息,我們沒有找到想要的結果,那麽問題出現在哪裏。
把上面幾個原因都排除掉了,難道真是遇到OracleBUG了麽。
有的時候分析問題會陷入一些誤區,比如一個數據庫出現大量的latch競爭導致會話飆升然後把process撐滿,從timemode裏面來看的話可以發現95%都是花費在了連接上面,那麽到底是大量不正常的連接(比如連接泄漏)導致了數據庫出現競爭呢,還是數據庫出現問題導致會話不能等了然後不停的重連導致了問題呢。
從這個庫這個時間點的timemode可以發現75%的dbtime都是花費在了解析上面,這也是沒有問題的因為這個時間點數據庫競爭就出現在解析上面,但是為什麽其中有38%的dbtime發生在解析失敗上面呢,也就是總共解析的一般時間都是錯誤的解析。硬解析只有5%左右。
我們來看一張正常時間點的timemode。
從這個趨勢圖庫看到解析失敗一直是跟著硬解析的次數而增加,並且每天都在上班之後開始發生。
數據庫正常時間點硬解析也只有不到5%左右,也就是硬解析沒有大的變化,但是解析失敗確認翻了幾倍。是什麽原因導致這麽多的解析失敗呢?另外解析失敗的SQL是否會導致大量latch競爭?解析失敗的SQL是否會在共享池中存儲?怎麽查詢到解析失敗的SQL?
很多時候我們會有這樣一個誤區,既然語法錯誤或者對象不存在應該在語法語義檢查這個步驟就掛了怎麽還好存在共享吃裏面呢?帶著這個幾個問題我們做幾個簡單的測試。
我們先了解下什麽是解析失敗的SQL。
那麽怎麽證明就是解析失敗的SQL存在共享池中並且在解析的時候持有librarycachelatch呢?
做下面測試之前我們先回顧一個Oracle一些基本概念。
Librarycache是sharedpool中的一塊內存區域,主要作用就是緩存執行過的SQL語句所對應的執行計劃信息等信息。當同樣的SQL再次執行時候可以直接利用已經緩存的相關對象不需要再從頭解析。
Librarycache對象句柄是以hashtable的方式存儲的,存儲方式如下圖:
當sql執行時候,首先會對sql文本進行hash運算然後根據hash值去相關hashbucket中遍歷,如果找到了就直接用該sql緩存的執行計劃等,如果找不到則從頭解析,並把解析後執行計劃等緩存在hashbucket中。
下面這幾張圖片展示了一個SQL解析的過程。
SQL的內存結構
我們知道SQL語句必須至少是一個父遊標一個子遊標存在的,當然生產中很多情況下都是一父多子的情況。
父遊標與子遊標結構是一樣的,區別在於sql文本存儲在父遊標對應的對象句柄中,而sql的執行計劃等信息存儲在子遊標對應的庫緩存對象句柄heap6中。另外父遊標的heap0中存儲著子遊標的句柄地址。如果解析錯誤的SQL在共享池中存儲的話那麽必然要產生一個父遊標然後父遊標裏面存儲的有SQL文本之類的信息,但是子遊標的?既然解析失敗那麽就沒有產生執行計劃。
關於heap0中信息可以參考如下圖:
父遊標句柄對地址可以在x$kglob視圖中查詢到,KGLHDPAR=KGLHDADR的記錄為父遊標
X$KGLOB
該視圖定義為[K]ernel[G]eneric[L]ibraryCacheManager
KGLHDADRRAW(4|8)Addressofkglhdforthisobject
該地址000007FF11937C90為select*fromenmoSQL的父遊標的句柄地址。
可以看到:
KGLOBHD0RAW(4|8)Addressofheap0descriptor
KGLOBHD6RAW(4|8)Addressofheap6descriptor
上面查到的就是該SQL父遊標的信息,父遊標的kglobhd0的地址為0000000075489AE8
該句柄地址記錄的信息很多包含了子遊標的信息。
找下該SQL子遊標的信息:
子遊標heap6的地址為000000007625FBF8句柄中存儲的也就是執行計劃相關的信息。
通過以上測試我們很容易找到sql的父遊標的句柄還有子遊標的句柄在內存中的地址。
下面做另外一個簡單的測試解析錯誤的SQL是否有父遊標還有子遊標生成。
可以看到是可以查詢到信息的,也就是有父遊標的句柄為00000000754453B8heap0的地址為0000000075485620.
可以看到是有錯誤的文本信息的內存地址,但是子遊標呢?
可以看到是沒有子遊標產生的,因為該SQL執行錯誤不會有執行計劃相關信息出現。
從x$kglob也可以查到kglobhd0kglobhd6都為空。
在x$kglcursor_child視圖也查不到任何信息的,v$sqlv$sqlare類似的視圖也就查不到解析錯誤的SQL了。
關於解析錯誤的SQL是否需要獲取latch其實從上面的測試已經證明了還是要獲取sharedpool的latch的因為生成了父遊標。
回顧以下SQL硬解析過程中需要獲取的latch.
首先持有librarycachelath,在librarycache相關hashbucket中掃描已經緩存的對象句柄,查找是否有匹配的父遊標,沒有找到釋放librarycachelatch.
接著持有librarycachelatch然後不釋放情況下持有sharedpoollatch從sharedpool中申請分配內存成功後是否sharedpoollatch再是否librarycachelatch.
還以上面那個錯誤的SQL為例做一個簡單的測試。
首先獲取librarycachelatch然後運行sql查詢。
這個時候會話已經hang了。
怎麽找到解析失敗的SQL?
通過關聯x$kglcursorx$kglcursor_child_sqlid這兩個視圖是可以找到解析失敗的SQL
通過使用Oracle10035event事件也是可以找到解析失敗的SQL
通過oraclesystemdump也可以找到解析失敗SQL
當然最後該問題定位到了相關解析失敗的SQL,該SQL主要是在月底某一模塊批量跑的時候大量的執行,最後修改應用程序代碼解決了問題。
通過這個簡單的案例可以看到不規範的開發習慣給數據庫帶了嚴重的性能影響。像類似這種解析出錯的SQL在很多客戶核心系統中比比皆是但是由於種種原因不能及時去除類似的SQL最終將帶來災難性的影響。
(轉載)處理SQL解析失敗導致share pool 的爭用