Oracle後臺專家解決library cache鎖爭用的終極武器

今天來給大家分享一個Oracle使用中的小技巧。 當某條SQL語句或者對象被反覆訪問,過多的軟解析可能會造成大量的“library cache:mutex X”爭用,有什麼樣的方法處理此類問題呢?這是個頭疼的問題。 今天的話題,就是介紹如何利用hotcopy來緩解library cache中的熱點爭用。在oracle 11g中,“library cache:mutex X”是個有點特殊的mutex,因爲它存在於幾個不同的位置,需要進一步的信息進行分辨。這裏還會簡單描述library cache的結構,理解library cache對認識oracle是非常重要的。

 

 

解決性能問題沒有特別的方法,主要靠對數據庫整體結構的準確理解。瞭解各種等待發生的原因,以及其在整個體系中的位置,才能做出正確的判斷。 所以我會先大致介紹下10G和11G對library cache這個結構的保護方式。 這樣你就會理解這個問題發生的可能原因。 然後,我們來嘗試通過創建對象的hotcopy來緩解這類爭用。

 

其中個別內容是我自己摸索和猜測出來的,難免有錯誤和不準確的地方,歡迎大家指正。

 

 

也許不是所有的同學都清楚library cache的結構,先在這裏簡單描述一下。

 

我們知道,當編譯過的SQL已經存在於library cache中的時候,oracle會進行軟解析,重用這個結構。 那麼oracle如何知道是否已經存在可用的SQL呢? 是通過如圖中的Hash結構。所有已經編譯過的library cache object會被組織在一個hash結構中。 當進行搜索時, Oracle會對SQL語句進行hash計算,根據結果找到相應的hash bucket,比如bucket 6, 然後再搜索鏈接在這個bucket上的所有handle,這裏你可以認爲每個handle對應唯一的SQL文本。

 

一種情況,如果“Handle X” 正好是要找的SQL,則會向內搜索“Handle X”包含的child,尋找可用的cursor。 如果有完全符合條件的可用child,則重用,完成軟解析。 如果沒有,則創建一個新的child,完成一次硬解析。

 

另一種情況,如果沒有找到符合條件的handle,說明SQL 文本是新的,則需要創建一個新的handle和第一個child。 同樣完成一次硬解析。

 

 

那麼爲什麼要在這個結構上進行鎖保護呢?什麼操作需要加鎖?

 

一個很簡單的例子,如果沒有鎖的話,假設process 1和process 2同時執行同一個SQL,當他們都沒有找到匹配的Handle時,有可能重複添加針對同一SQL的Handle Y。

 

同樣的,當兩個進程同時搜索Handle X下是否有匹配的Child時,也有可能在此Handle下,添加兩個相同的Child。

 

因此,爲了避免這種情況發生,對此結構的鎖是必要地。 由於這些操作都是快速的內存操作,此類鎖不宜採用複雜的結構,沒有共享模式和獨佔模式之分。 所以,只有一個進程可以對某個結構進行搜索和修改。也就是說,即使是軟解析,在結構內進行搜索時,也是串行的。

 

在10g和11g,library cache鎖的粒度是不同的。

 

 

10G的時候,保護這個結構的是 latch: library cache。 這個latch的默認數量爲大於CPU Count的最小質數,而默認最小的bucket個數爲509,這就意味着每個latch要保護多個Hash Buckets以及相應的handle。 也就是說,對於一組bucket和handle,每次只有一個進程可以搜索修改。因此,這個latch經常成爲熱點。

 

 

11G以後,此latch被替換爲library cache: mutex X。鎖的粒度變小,每個bucket和每個handle都被一個單獨的mutex保護,大幅度減小了爭用

 

然而,從latch名字本身,我們區分不出等待是發生在hash bucket上,還是handle上。需要通過p1來進行分辨。

 

 

如果P1是bucket的編號,通常比較小。 如果下面查詢返回結果,說明爭用發生在hash bucket上。 返回結果爲鏈接在此bucket上的對象。

 

select KGLNAHSH, substr(KGLNAOBJ, 1, 30) Name,

         KGLHDNSP Namespace, KGLNAHSV Hash

from x$kglob where KGLHDBID = &p1;

 

這種情況下,鎖是在bucket上,即對handle進行訪問操作時發生的。

 

實際上,這種情況並不常見,因爲每個bucket上鍊接到的handle通常不會很多。 爲了提高hash表的效率,當bucket上平均handle數量超過2時,oracle會將bucket的個數翻倍,重建hash表,用空間交換時間。 在鏈不長的情況下,一般handle上會先遇到瓶頸。

 

 

更常見的爭用位置,是在 handle上,這說明某個特定cursor或者object被搜索時發生爭用。

 

如果下面查詢返回結果,則說明是這種等待。返回結果爲handle所對應的對象。 可能是cursor,也可能是object

 

    select kglnaown, kglnaobj

      from x$kglob

     where kglnahsh = &p1;

 

 

明白了大致的原理之後,就可以想象到這個mutex爭用的可能原因。

 

首先,它有可能是硬解析造成的。 當發生硬解析時,無論要創建一個新的handle,還是要創建一個新的Child,都需要額外大量的時間來完成,相應地,對這個mutex的持有時間會明顯變長,造成爭用。

 

由於硬解析會向shared pool申請大量的內存,因此這種情況會伴隨一系列其他等待如 latch: shared pool, library cache load lock。 解決辦法就是分析硬解析過多,或者version count過高的問題。

 

還有一種情況,就是OS資源不足,特別是CPU資源緊張。 這種情況下,oracle進程無法獲取足夠的資源去完成相應的工作,無法及時釋放此mutex,造成請求的堆積。

 

 

再有,就是某個持有者長時間不釋放此mutex,造成請求的積壓。這是不正常的, 需要對mutex持有者的當前狀態進行分析。 Errorstack, process dump。 如果持有者被其他進程阻塞,則通過hanganalyze等工具繼續查找最終持有者。

 

另外,個別Oracle bug也會造成此類問題。

 

如果,以上原因都被排除掉,那麼,對某熱點SQL或熱點object的頻繁訪問,也會造成此問題。 也就是我們之前說過的,哪怕是軟解析,也會在查找library cache結構的時候串行。 這類問題比較讓人頭疼,一方面,SQL語句來自業務,我們不能人爲減少SQL的執行次數和效率。 另一方面,如果SQL的文本相同,就一定會被hash到一個特定的handle上,這個是無法改變的。從前遇到這類問題非常讓人頭疼,一般可能的解決辦法見下一頁。

 

 

那麼接下來該怎麼辦呢?

 

首先,可以查下是否有連接風暴。 連接風暴可能造成某些PLSQL對象的頻繁執行,是造成此類問題的常見原因之一。

 

Version count如果很高,也會引發這個問題,因爲找到handle以後, 對child的搜索是用遍歷的方式,如果version很高child很多,則每次搜索的時間很可能會變長。會造成對handle上的鎖,持有的時間也變長。所以,這也是一個可能的原因。

 

另外,和開發聯繫,研究SQL或某個對象如此高的併發是否正常的。 如果SQL來自不同的模塊,則可以試着修改來自不同位置的SQL文本(比如加空格,或無用註釋),使他們hash到不同的handle上。

 

增加session_cache_cursors是個可能的嘗試,如果cursor被cache住,child的位置會被保存在pga中,那麼查找child的速度會明顯加快,對Mutex的持有時間也會變小。

 

如果以上方法均不適用,我們最後可以嘗試創建hotcopy來改善此問題。

 

 

對於資源爭用的問題,解決辦法只有兩個,要麼減小需求,要麼增加資源數量。而hotcopy的原理,實際就是在hash SQL文本的時候,加上PID的部分。 這樣,哪怕執行相同的SQL文本,不同進程就有可能會被hash到不同的handle上。 這實際增加了資源的數量。當然,copy的數量是可控的。

 

製作了hotcopy以後,相同的對象和child可能多次出現在library cache的不同位置,造成了一部分空間浪費,但卻可以解決library cache中熱點對象或熱點cursor的爭用。也是一種用空間換時間的辦法。

 

 

hotcopy這個功能是通過fix 9239863和fix 9282521引入的。 11.2.0.2以上版本可以直接使用。 低版本數據庫需要安裝相應地補丁。 針對11.2之前版本的數據庫,需要設置兩個隱含參數。 slide裏有詳細的描述。針對cursor 和 object對應不同的方法。

 

 

11.2之後,可以通過標準API進行設置。

http://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_shared_pool.htm#ARPLS68085

 

 

下面我們用一個簡單的實驗,查看到底發生了什麼。 在試驗中,我模擬了一個場景,大量session反覆執行某一個SQL。

 

從查詢結果來看,內存中ASH捕捉到的等待事件, library cache: mutex X 佔據了很高的比例。

 

 

AWR報告反應了類似的內容。等待進入了top 1。

 

 

經查詢,library cache: mutex X等待的P1非常集中。

 

 

經過驗證,P1的值指向該條cursor的handle。 其實你看這個數字的長度,就知道不可能是hash bucket。 這個handle所在的bucket是14503。

 

 

接下來, 我們用之前介紹的方法來激活基於此cursor的hotcopy。

 

由於我的實驗機器CPU太少,所以 我提前設置了隱含參數”_kgl_hot_object_copies”=4來指定copy的個數。

 

在這裏我們使用了11.2提供的API進行hotcopy的實施,針對cursor,需要完整的hash,可以從X$KGLOB.KGLNAHSV中獲取。

 

然後再次提交同樣的負載。

 

 

這一次,我們看到,ASH捕捉到的mutex等待開始分散到多個P1上,總數量在變少(忽略32127143,這是第一次試驗遺留的結果)。 通過進一步驗證能夠確認,每個handle的內容都是這個cursor。

 

 

 

 

這裏我也注意到14503這個hash bucket上的爭用增加,如果你還記得之前的輸出,這其實是修改前那個cursor所在的hash bucket。 我猜測即使做了hotcopy,請求仍舊會經由這個bucket跳轉。由於handle上的請求處理速度變快了, bucket上的爭用開始顯現,從library cache的結構來看,這些是合理的。

 

 

從library cache dump 中驗證,的確增加額外4個handle,相同的對象名。

 

 

在新生成的AWR報告中, library cache: mutex X 等待時間下降了50%, 從1882秒,下降到942秒.  由此可以驗證,針對純粹的library cache的熱點爭用,hotcopy是可以進行緩解的。

 

 

最後做一個簡單地小結。

 

首先,從library cache的結構看, 即使是軟解析,對child和handle的搜索,也是串行的。 雖然這個操作速度很快,但仍會造成熱點爭用。

 

另外,造成library cache: mutex X爭用的原因很多,只有純粹的熱點爭用才需要用hotcopy來解決。

 

最後,以我的經驗來看,解決數據庫性能問題,沒有固定的套路。 Oracle是個極其龐大複雜的體系,幾乎不可能通過幾個簡單的等待事件推測出根本原因,不會有這樣的“神”。 正確的分析方法是,儘可能多的收集證據,然後利用自己的知識和對整體結構的理解,推導出一個“故事”把所有證據串聯起來,有了“故事”以後,再收集更多的證據,反過來來驗證故事的正確性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章