深入理解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利用率非常高,但是吞吐量卻很低,典型的“出工不出活”


參考至:《讓Oracle跑得更快》譚懷遠著
           http://blog.csdn.net/tianlesoftware/article/details/5263238
           http://space.itpub.net/35489/viewspace-670623
            http://www.alidba.net/index.php/archives/106 
本文原創,轉載請註明出處、作者
如有錯誤,歡迎指正
郵箱:[email protected]


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