當前位置:
首頁 > 最新 > Oracle 資料庫中enq:TX-index contention等待時間淺析

Oracle 資料庫中enq:TX-index contention等待時間淺析

前言:

該文來自於Oracle RWP團隊的領袖Andrew Holdsworth的一個精彩分享,筆者覺得很精彩,便將其梳理成文,並加上了自己的理解,希望能幫助大家更好的理解TX-index contention等待事件。

enq:TX-index contention:

enq:TX-index contention是一個非常常見的等待事件,其專指由於索引分裂產生的競爭等待。最常見的索引競爭一般發生在主鍵索引上,主鍵值從序列(sequence)中獲取,每個事務都會生成一條新的記錄,每條記錄都要獲得一個新的序列號,因為從sequence中取出的值是單向遞增的,當索引中插入數據,並且維護索引結構的時候,不得不一直走向索引的最右側的分支,對於每一個操作,都會想要維護索引中最右邊的葉節點,那麼所有的操作都會關注同一個內存塊,希望能夠維護這塊內存,這就是一種典型的競爭形式。但在同一時間,只有一個人能夠修改這塊內存,因此當有一個人在修改的時候,其他所有想修改的人只能處於等待狀態。

在一個並發量很大的OLTP系統中,開發者希望記錄每個用戶的登錄和操作情況,因此需要創建基於主鍵的索引。接下來我們將通過實驗說明如何減少這類索引競爭。

首先來看在沒有索引的情況下系統的運行狀況,系統運行平穩並且性能良好。如下圖所示:

接下來我們嘗試最簡單的B樹索引,索引的的值是通過sequence生成的。在做插入操作的時候發現對性能產生嚴重的影響,響應時間變長,吞吐量直線下降,CPU利用率也從60%下降到20%。查看等待事件,發現大量的enq:TX-index contention,由此說明B樹索引並不合適。

為解決熱點塊的爭用,反向索引或許是不錯的選擇。例如表的某一列:.....1234,1235,1236,1237......,建立正向索引:......1234,1235,1236,1237.......,這四行放在同一個leaf block中,建立反向引,...4321,5321,6321,7321...,這四行放在四個不同的leaf block,這有效的讓對一個塊的熱點操作分散開來,從而避免索引競爭問題。接下來我們引入反向索引,觀察一下系統的運行情況:

由上圖可見,性能問題立即得到改善,系統的相應時間立即減小,TPS得到回升,CPU使用率提高。運行一個禮拜後,繼續觀察。

在運行一段時間後又發生了性能問題,系統中出現了大量跟I/O相關的等待事件,反向索引有一個弊端就在於索引的CF會變的越來越大,導致我們要維護索引時必須將很多個索引塊放入到buffer cache中,最後SGA裝不下的時候,就需要通過磁碟I/O來維護索引,與此同時還會將其他的索引和表都擠出內存,當其他應用獲取不到內存時就需要從磁碟上讀取數據,而磁碟I/O的時間是毫秒級別,也就是說從磁碟讀取數據所花的時間是從內存讀取的一千倍。我們實際上所做的只是把導致系統變慢的等待事件替換成另一種等待事件而已。

B樹索引會導致內存的競爭和等待,而反向鍵索引則會導致太多的I/O,接下來嘗試第三種方案:索引分區,通過hash將索引分成一個一個小塊,這樣競爭就不會聚集在最右邊的節點上,也就是說,我們用一些小的競爭代替右邊節點上的集中的熱點競爭,看看能不能減少I/O,同時讓系統的響應時間和CPU使用率回歸到正常的水平。

在單實例上,通過hash分區索引在一定程度上緩解了插入數據的競爭問題,但我們在進行性能優化的過程中,總是很謹慎使用hash 分區索引,原因有很多。首先我們在對原來沒有分區的表進行分區,這是有風險的,比如可能會引起執行計劃改變,一些非插入的操作的性能下降等等。除了這個問題之外,還為整個系統帶來了一些擴展性問題。

接下來將之前的單實例增加到兩個節點,連接數和處理的事務數都增加到原來的兩倍,硬體配置增加到兩倍,同樣使用hash分區索引,觀察性能和吞吐量會如何變化。

當我們看到第二個節點起來之後,觀察到第一個節點的CPU使用率有所降低,但單純從吞吐量來看基本上是看不出性能的變化的。接下來我們看到響應時間變長了,出現了一些新的等待事件,都是一些GC buffer的事件。

通過hash分區索引我們使得負載分散到了索引的所有葉節點上了,如果想修改的葉節點所在的數據塊正好在當前節點的Buffer Cache中,那麼修改的時間就是微秒級的,但如果要修改的數據在其他節點上,那就需要通過網路將該數據讀取到當前內存中,隨著節點的擴展,這些數據塊存在於當前實例buffer Cache的可能性就會降低,兩個節點,50%,三個節點33%,依次類推。

到這裡,總結一下我們所面臨的挑戰:一是實例間的競爭或者說擴展性問題。二是單節點間的競爭。有沒有一種方法。既能解決單節點的競爭問題,又能在擴展中不帶來新的問題,這就需要保證緩存的相關性,讓數據所在的實例恰好是會被訪問的實例。

如果能控制生成代理主鍵,我們就能把這些特徵放入到生成的主鍵中,這樣不僅能夠保證得到較好的緩存相關度,從而使RAC可擴展,而且可以把主鍵分散開,這樣在單實例上也不會出現競爭。

首先要考慮的是可以使用實例號作為主鍵號的開頭,這樣插入數據的時候就會保存在樹節點的一邊,也正是這些數據應該被保存到的實例上,這樣就可以建立與插入操作相關的緩存相關性。當我們在訪問的時候能夠準確定位數據所在的實例之後,第二個要考慮的問題就是,訪問同一個實例上數據的時候不會競爭同一塊內存,如果說智能主鍵的中間部分如果是對進程號某種方式取余,這樣就把對索引的維護分散到同一實例的多個內存塊上去。而智能主鍵的最後一部分是sequence的本身,這樣可以保證引用和完整性,確保每一行都是唯一的。因此最終智能主鍵的組成是:實例ID-進程號取余-序列號

在使用智能主鍵後,觀察系統的運行情況:

發現系統的響應時間減少,其他等待事件消失,CPU利用率提高,並且只有CPU在佔用時間。跟最初系統沒有產生競爭的情況下的性能一樣。

Oracle 18c Scalable Sequence新特性:

值得注意的是,oracle在最新發布的資料庫版本中引入了可擴展序列的概念。如下所示:

可擴展序列 - Scalable Sequence:通過在CREATE SEQUENCE或ALTER SEQUENCE語句中指定SCALE子句,可以使序列獲得健壯的擴展性。

我們來看一下 18c 中的可擴展序列的定義:

通過以下語法定義 scalable sequence:

CREATE | ALTER SEQUENCE sequence_name

...

SCALE [EXTEND | NOEXTEND] | NOSCALE

當 SCALE 語句被指定時, 一個 6 位數的數字被指定作為序列的前綴,末尾、是正常的序列數字,兩者聯合成為新的序列:

scalable sequence number =6 digit scalable sequence offset number||normal sequence number

在這裡,6位數字正是由 實例號||會話號 生成的:

6 digit scalable sequence offset number = 3 digit instance offset number || 3 digit session offset number.

The 3 digit instance offset number is generated as [(instance id % 100) + 100]. The 3

digit session offset number is generated as [session id % 1000].

現在通過這種序列方式,能夠真正將來自不同實例的數據分散開來,索引競爭大大降低,從而提升了性能,使得序列變得可擴展。

開放系統支持部@ABCDC 胡偉帥

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 OpenPower 的精彩文章:

TAG:OpenPower |