天天看點

深入了解Oracle中的latch

串行化 概述 

串行化 - 資料庫系統本身是一個多使用者并發處理系統,在同一個時間點上,可能會有多個使用者同時操作資料庫, 多個使用者同時在相同的實體位置上寫資料時,不能發生互相覆寫的情況,這叫做串行化,串行化會降低系統的并發性,但這對于保護資料結構不被破壞來說則是必需的。在Oracle資料庫中,通過闩鎖(latch)和鎖定(lock)來解決這兩個問題。

闩鎖和鎖定既有相同點又有不同點。相同點在于它們都是用于實作串行化的資源。而不同點則在于闩鎖(Latch)是一個低級别、輕量級的鎖,獲得和釋放的速度很快,以類似于信号燈的方式實作。而鎖定(Lock)則可能持續的時間很長,通過使用隊列,按照先進先出的方式實作。也可以簡單地了解為闩鎖是微觀領域的,而鎖定則是宏觀領域的。

注意 :latch是用于保護SGA區中共享資料結構的一種串行化鎖定機制。它不僅僅用于buffer cache, 還用于shared pool以及log buffer等。 

闩鎖(latch)概述 

Oracle資料庫使用闩鎖(latch)來管理SGA記憶體的配置設定和釋放.Latch是用于保護SGA區中共享資料結構的一種串行化鎖定機制。Latch的實作是與作業系統相關的,尤其和一個程序是否需要等待一個latch、需要等待多長時間有關。

Latch是一種能夠極快地被擷取和釋放的鎖,它通常用于保護描述buffer cache中block的資料結構。與每個latch相聯系的還有一個清除過程,當持有latch的程序成為死程序時,該清除過程就會被調用。Latch還具有相關級别,用于防止死鎖,一旦一個程序在某個級别上得到一個latch,它就不可能再獲得等同或低于該級别的latch。

Latch 不會造成阻塞,隻會導緻等待。 阻塞是一種系統設計上的問題,等待是一種系統資源争用的問題。 

SPIN與休眠 

SPIN

在performance tuning的sg上tuning contention章裡看到的,原文是這樣的:Latch behavior differs on single and multiple CPU servers. On a single CPU server, a process requesting an in-use latch will release the CPU and sleep before trying the latch again. On multiple CPU servers, a process requesting an in-use latch will “spin” on the CPU a specific number of times before releasing the CPU and trying again. The number of spins the process will use is OS specific.

spin 就是一個程序獨占cpu time,直到運作的結束。這個期間其他程序不能獲得這個cpu的運作時間。對于單CPU來說沒有spin概念。

比如資料緩存中的某個塊要被讀取,我們會獲得這個塊的 latch,這個過程叫做spin,另外一個程序恰好要修改這個塊,他也要spin這個塊,此時他必須等待,目前一個程序釋放latch後才能spin住,然後修改,如果多個程序同時請求的話,他們之間将出現競争,沒有一個入隊機制,一旦前面程序釋放所定,後面的程序就蜂擁而上,沒有先來後到的概念,并且這一切都發生的非常快,因為Latch的特點是快而短暫。

休眠

休眠意味着暫時的放棄CPU,進行上下文切換(context switch),這樣CPU要儲存目前程序運作時的一些狀态資訊,比如堆棧,信号量等資料結構,然後引入後續程序的狀态資訊,處理完後再切換回原來的程序狀态,這個過程如果頻繁的發生在一個高事務,高并發程序的處理系統裡面,将是個很昂貴的資源消耗,是以Oracle選擇了spin,讓程序繼續占有CPU,運作一些空指令,之後繼續請求,繼續spin,直到達到_spin_count值,這時會放棄CPU,進行短暫的休眠,再繼續剛才的動作。初始狀态下,一個程序會睡眠0.01秒。然後醒過來,并再次嘗試獲得latch。 程序一旦進入睡眠狀态,則會抛出一個對應的等待事件,并記錄在視圖v$session_wait裡,說明目前該程序正在等待的latch的類型等資訊。

Latch的種類 

願意等待(Willing-To-Wait)

大部分的latch都屬于這種類型(Willing-To-Wait)。這種類型的latch都是通過Test-And-Set的方式來實作的。 

任何時候,隻有一個程序可以通路記憶體中的某一個資料塊,如果程序因為别的程序正占用塊而無法獲得Latch時,他會對CPU進行一次spin(旋轉),時間非常的短暫,spin過後繼續擷取,不成功仍然spin,直到spin次數到達閥值限制(這個由隐含參數_spin_count指定),此時程序會停止spin,進行短期的休眠,休眠過後會繼續剛才的動作,直到擷取塊上的Latch為止。程序休眠的時間也是存在算法的,他會随着spin次數而遞增,以厘秒為機關,如1,1,2,2,4,4,8,8,。。。休眠的閥值限制由隐含參數_max_exponential_sleep控制,預設是2秒,如果目前程序已經占用了别的Latch,則他的休眠時間不會太長(過長會引起别的程序的Latch等待),此時的休眠最大時間有隐含參數_max_sleep_holding_latch決定,預設是4厘秒。這種時間限制的休眠又稱為短期等待。

另外一種情況是長期等待鎖存器(Latch Wait Posting),此時等待程序請求Latch不成功,進入休眠,他會向鎖存器等待連結清單(Latch Wait List)壓入一條信号,表示擷取Latch的請求,當占用程序釋放Latch時會檢查Latch Wait List,向請求的程序傳遞一個信号,激活休眠的程序。Latch Wait List是在SGA區維護的一個程序清單,他也需要Latch來保證其正常運作,預設情況下share pool latch和library cache latch是采用這個機制。

如果将隐含參數_latch_wait_posting設定為2,則所有Latch都采用這種等待方式,使用這種方式能夠比較精确的喚醒某個等待的程序,但維護Latch Wait List需要系統資源,并且對Latch Wait List上Latch的競争也可能出現瓶頸。

如果一個程序請求,旋轉,休眠Latch用了很長時間,他會通知PMON程序,檢視Latch的占用程序是否已經意外終止或死亡,如果是則PMON會清除釋放占用的Latch資源。

總之, Latch擷取的流程:請求-SPIN-休眠-請求-SPIN-休眠 ... ... 占用。

不等待(No-Wait)

這種類型的latch比較少,對于這種類型的latch來說,都會有很多個可用的latch。當一個程序請求其中的一個latch時,會以no-wait模式開始請求。如果所請求的latch不可用,則程序不會等待,而是立刻請求另外一個latch。隻有當所有的latch都不能獲得時,才會進入等待。

latch的擷取過程可以用下圖來概括

Latch和Lock的差別 

Latch是對記憶體資料結構提供互斥通路的一種機制,而Lock是以不同的模式來套取共享資源對象,各個模式間存在着相容或排斥,從這點看出,Latch 的通路,包括查詢也是互斥的,任何時候,隻能有一個程序能pin住記憶體的某一塊,幸好這個過程是相當的短暫,否則系統性能将沒的保障

Latch隻作用于記憶體中,他隻能被目前執行個體通路,而Lock作用于資料庫對象,在RAC體系中執行個體間允許Lock檢測與通路

Latch是瞬間的占用,釋放,Lock的釋放需要等到事務正确的結束,他占用的時間長短由事務大小決定

Latch是非入隊的,而Lock是入隊的

Latch不存在死鎖,而Lock中存在。 

Latch的cleanup

在latch的使用過程中,可能會出現一些異常,而導緻有些latch被異常占有得不到釋放,這樣就會有問題了,别的程序過來請求不到。出現這樣的異常pmon程序會跟進處理,對于其處理的流程來說,最重要的莫過于将沒有送出的事物復原,那麼就需要latch支援恢複,那麼latch在開始操作前會先寫一些資訊去latch的恢複區。Pmon 3秒鐘會自動運作一下,但是這也是很長的一段時間了,是以在程序在請求一個latch失敗多次之後,會post pmon程序去check一下占有這個latch的process是不是正常。

Latch的level 

Latch的級别分為0~14,共15個級别,0級最低,14最高。如果兩個latch之間有聯系,隻能請求level更高的latch。原因如下:

如果a程序占有一個level 為5的latch,它去請求一個level為3的latch,而程序b,占有這個level為3的latch,又去請求那個level 為5的latch,這樣會有什麼問題呢?因為它是可以去spin的,又是可以去sleep的,sleep之後還是繼續重複,那就永遠沒有完沒有了了。是以呢,level的request是有level順序的,不能随便的請求,隻能由低級的latch去請求進階的latch。

如果經常a一定要申請程序b的latch的話,隻能放棄原有latch level5為的latch重新對b程序的latch進行申請。

Latch資源争用 

如果latch資源被争用,通常都會表現為CPU資源使用過高。 

而反過來說,如果我們發現CPU資源很緊張,使用率總是在90%以上,甚至總是在100%,其主要原因有以下幾點。 

1、SQL語句 如果沒有使用綁定變量,很容易造成頻繁讀寫shared pool裡的記憶體塊,如果存在大量的SQL被反複分析,就會造成很大的Latch争用和長時間的等待, 進而導緻與解析SQL相關的共享池中的Latch争用 。與 shared pool共享池相關的latch有Library Cache Latch 和Shared Pool Latch。如果資料庫出現了上述latch的争用,則有必要檢查下是否有正确使用綁定變量

2、 通路頻率非常高的資料塊被稱為熱快(Hot Block),當很多使用者一起去通路某幾個資料塊時,就會導緻 資料緩沖池Latch 争用,最常見的latch争用有 :buffer busy waits和cache buffer chain

Cache buffer chian:

當一個會話需要去通路一個記憶體塊時,它首先要去一個像連結清單一樣的結構中去搜尋這個資料塊是否在記憶體中,當會話通路這個連結清單的時候需要獲得一個Latch,如果擷取失敗,将會産生Latch cache buffer chain 等待,導緻這個等待的原因是通路相同的資料塊的會話太多或者這個清單太長(如果讀到記憶體中的資料太多,需要管理資料塊的hash清單就會很長,這樣會話掃描清單的時間就會增加,持有chache buffer chain latch的時間就會變長,其他會話獲得這個Latch的機會就會降低,等待就會增加)。

Buffer busy waits:

當一個會話需要通路一個資料塊,而這個資料塊正在被另一個使用者從磁盤讀取到記憶體中或者這個資料塊正在被另一個會話修改時,目前的會話就需要等待,就會産生一個buffer busy waits等待。

産生這些Latch争用的直接原因是太多的會話去通路相同的資料塊導緻熱快問題,造成熱快的原因可能是資料庫設定導緻或者重複執行的SQL 頻繁通路一些相同的資料塊導緻。熱塊産生的原因不盡相同,按照資料塊的類型,可以分成以下幾種熱塊類型,不同熱塊類型處理的方式都是不同的:表資料塊、索引資料塊、索引根資料塊和檔案頭資料塊。

3、另外也有一些latch等待與bug有關,應當關注Metalink相關bug的公布及更新檔的釋出。為何latch的争用會引起CPU使用率較高呢?

其實很容易了解,比如程序A持有latch,此時程序B也需要持有相關latch,但是沒有獲得,這時候程序B就需要進行spin,如果類似程序B的程序較多的話,對CPU進行spin的程序就會較多,表現也就是CPU使用率非常高,但是吞吐量卻很低,典型的“出工不出活”

本文轉自 wangergui 51CTO部落格,原文連結:http://blog.51cto.com/wangergui/1844950,如需轉載請自行聯系原作者