<a target="_blank" href="http://www.itpub.net/thread-1424719-1-1.html">http://www.itpub.net/thread-1424719-1-1.html</a>
(
入門1)
一直想點文章關于Latch的,又一直沒寫,一是因為懶,二是一直覺得現在關于Latch的書那麼多,還有必要寫嗎?
後來,看到了一些文章:
latch産生質疑。
2.某帖,做了buffer busy wait和cache buffer chain的實驗,得出很多結論。可從頭到尾,文章的實驗中,buffer busy wait和cache buffer chain都是同時出現的。如果真的那麼清楚這兩者的差別,能夠模拟出一種大量buffer busy wait卻沒有任何cache buffer chain的實驗嗎?
如果你也有這些問題,你可能也跟我曾經一樣,似懂非懂。是以,終下決心寫點東西,以做分享,希望對菜鳥們有些幫助(高手請一笑而過):
請往下看,注意,再次聲明,本文不是介紹各種各樣latch的進階教程。本文隻講基礎,那就是,你知道太多種latch了,可是,你真的知道什麼是latch嗎?
這點,就要從我們為什麼需要latch上來說了,latch到底在保護什麼?如果沒有latch,什麼樣的操作會損壞latch想要保護的結構?
(資料結構第一章:連結清單。)
答:latch保護的是連結清單。會損壞連結清單的,不是對于連結清單上的資料的讀寫操作---這些操作是完全可以并行的。
會損害連結清單結構的,是将連結清單上的塊摘下,或者插入新的塊的操作----這樣的操作,如果不存在并發控制,是會損壞連結清單的。
譬如,一個連結清單,a--b--c--e--
如過現在想往b與c間,插入新節點m。那麼我就需要更改b的next node的指針,以及c的previous node指針。
如果沒有latch做并發控制,那麼與此同時,如果另一個程序也向b與c(往a與b,c與e之間不會有問題)之間插入n,也需要去更改b和c的指針,那就有可能造成這條鍊被破壞,譬如結構變成:
a--b--n
m--d--e
這就是latch的意義,用于挂鍊,下鍊!
而對于我們的cbc latch,鎖控制的就是從disk上将block讀入記憶體中的鍊上,以及将鍊上的block摘下并寫回disk。
是以,latch隻關心你操作的目标block是否在鍊上---因為可能牽扯到上鍊下鍊,而不關心你這個操作本身是select還是insert還是delete還是update!cbc latch與操作是select還是DML沒有一毛關系!
是以,現在再回去看第一個問題----為什麼select還是會産生大量的buffer cache chain争用?
現在你看清了,貌似這個問題問的有根有據,其實,問的原因和結果是沒有聯系的兩碼事。
那麼shared latch是否真的存在嗎?cache buffer chain是可share的嗎?答案是:YES。并且完全可以通過實驗模拟出來清楚的看到他的作用。
----------------------------------------------------------------------------------
(入門2)
先以一個問題引入:
10g中v$latch的底層表X$KSLLD為例,他的全稱是
X$KSLLD: [K]ernel [S]ervice [L]? Management [L]atch [D]escriptor
請問,問号這裡的L,是什麼含義?猜測下,再往下看。
引入些補充知識:
對于作業系統來說,可以劃分為三種lock,我們稱之為OS Lock:
1.spinlock。最輕量級的lock,當擷取不到時,發生自旋,不用發生上下文切換。
2.mutex--互斥量。當擷取不到時,将發生上下文切換。好處是,不會使CPU發生空轉。但是如果頻繁的切換嚴重消耗資源。
3.semaphores--信号量。與mutex(互斥量類似),當擷取不到時,發生上下文轉換。但是與mutex的差別是,mutex互斥量,同一時間隻能被一個程序擷取,而信号量,可以同時好幾個程序擷取。
從上面我們可以看出,spinlock是最輕量級的而,oracle的latch是封裝在系統的spinlock之上。
而oracle也有mutex,11g中,oracle引入了更輕量級的mutex,以取代某些latch。你對此就沒有置疑過嗎?spin才是最輕量的啊,不用發生上下文轉換,mutex要上下文轉換的,怎麼可能比spin輕量?
MUTEX當然不可能比SPIN更輕量!oracle那麼說因為,oracle引入的mutex不是上面所說的OS mutex!而是同樣建構于spin之上KGX mutex。
扯遠了,我們繼續latch:
下面我們再來看X$KSLLD,
X$KSLLD: [K]ernel [S]ervice [L]? Management [L]atch [D]escriptor
問号處的[L],現在你知道了,這裡是Lock,但是如果沒有上面的知識,你很難了解這裡為什麼會是Lock。你會覺得,Lock怎麼又在這裡跟latch扯上關系?
事實上,我也看到某些大師将其翻譯成:
X$KSLLD: [K]ernel [S]ervice [L]Latch Management [L]atch [D]escriptor
因為沒有以上的知識,無法了解這裡為什麼又扯上了Lock。但是,有了上文的描述,你知道,這裡L就是lock,隻不過這個lock的意思不是
oracle裡面各種各樣的TX,TM等鎖,而是OS Lock,用來控制程序間的互動與并行。
繼續下去:latch是如何實作的,真的隻是記憶體中的一個标志位那麼簡單嗎?
很多人是這麼了解latch的,一個标志位:A。可以有兩種選擇:
A=0 或者 A=1。任何程序想要對共享的資源進行操作時,先去看A,如果是0,則代表未被使用,那麼置位成1,然後自己去使用,
如果是1,則等待。
就這麼簡單?
對的,你表面上看到的latch就這麼簡單,可是背後的故事,你知道嗎?
現在想想:為什麼我們要對共享資源加标志位?因為我們想以此控制對資源的串行通路。但是請别忘了,你這個标志位本身,也是一種資源,對這種标志位本身的通路,也是會發生沖突的!
再往微觀處想想:現在标志A為0,而有兩個程序P1與P2,在并行運作着,這時,同時要用到A。于是P1先将A從記憶體讀入,檢測A是0,于是準備将A置為1,在P1将A寫回記憶體前,P2也将A讀入,此時A仍然是0。好了,P2也發出指令将A置1。于是,P1,P2都認為是自己成功将A置為1的,擁有對資源的操作權。
然後呢?連結清單被破壞了。
謝謝大家的捧場,懶人為了兄弟們咬牙再寫點

(入門3)
這一節先回答第一節和第二節的幾個疑問:
1.如果真的這麼明白兩者的差別,能模拟出一個大量buffer busy waits競争卻沒有任何cbc latch争用的實驗嗎?
相信經過前面的描述,對latch到底是在做什麼應該有了一點了解了。
正如第一講中所闡述的,與select/DML相關的是buffer busy waits,而cache buffer chains與select/DML無關。
那麼cache buffer chains與什麼有關?
答:索引。準确的說,是唯一索引。
當一個操作,走的是唯一索引的話,那麼oracle預設目标block是存在于目标鍊上的,這時,oracle會以shared的模式去擷取
cbc latch。如果之後oracle發現弄錯了,這個目标塊不存在于鍊上。那麼這時,oracle再會嘗試以exclsive模式去獲得
cbc latch,以将block挂往鍊上。
但是,如果是全表掃描,oracle會預設的認為目标block肯定是不存在于目标鍊上的,oracle會直接以exclsive模式去獲得cbc latch。
這個實驗是老早以前證明cbc是shared latch做的。實驗我懶得再去做一遍了,相信大家看完後大家都能做出來,我就直接把實驗結果寫出來了。
當然,有興趣的朋友可以做下把過程貼出來

,我就偷懶下了,呵呵。
傳統的buffer busy waits實驗,這麼做,我們就可以完全搞清楚cache buffer chains與buffer busy waits。
兩個session,對于同一資料塊的操作。
a.表上無索引,DML操作。
實驗結果:大量cbc latch争用,大量buffer busy waits競争。
b.表上無索引,SELECT操作。
實驗結果:大量cbc latch争用, 無任何buffer busy waits競争。
c.表上有唯一索引,DML操作。
實驗結果:無任何cbc latch争用,大量buffer busy waits競争。
d.表上有唯一索引,SELECT操作。
實驗結果:無任何cbc latch争用,無任何buffer busy waits競争。
現在,shared latch是否存在,cbc latch到底起什麼作用,與buffer busy waits的差別,還要我多說什麼嗎?
2.第二節留下的問題:作業系統如何來做到操作FLAG A時防止并行帶來的沖突?
答:作業系統第二章:原子性操作。
當oracle調用spin函數的時候,作業系統把“一個cpu的讀取flag進入記憶體,檢查flag值,值位,寫回記憶體”封裝成一個
原子性操作。以保證這個操作不會被中斷。flag A不會同時被其他CPU修改。
那麼作業系統是如何做到這一點的呢?
答:通過鎖BUS LINE(總線)。
CPU到記憶體的結構是這樣的:cpu core+cache+Bus Line.
其中,Bus Line是公用的。你的CPU可以是N核,但是,BUS Line是所有CPU共享的。所有CPU與記憶體的互動都是
通過這個BUS LINE。是以,當一個CPU發出spin指令時候,會鎖住總線,以阻止其它CPU對記憶體的通路與操作。以此來保證對于标志位
的至位是原子性的。
其實這裡面門道還是很多的,還有spin本身也分成很多類型,oracle用的是其中比較原始的一種。不過這些就太貼近作業系統,離oracle太遠了。
是以這裡就不贅述了。有興趣的朋友們可以自己去查。
----------------------------------------------------------------------------------------------------
(入門4)
與前三節基本都是自己的研究不同,從這一節開始,很多東西都是外國佬的成果,不過我也做過實驗可以基本保證正确。
下面開始:latch是如何運作的。
在9i以前,oracle内部使用自旋--睡眠--自旋--睡眠模式來不斷嘗試對latch的擷取,每次的睡眠時間依次是:
0.01-0.01-0.03-0.03-0.08-0.07-0.16-0.23-0.4-0.39-0.73-0.72-1.39-1.38-2.04-2.04.....
但是,從9i開始,這種情況已經變了,latch不再使用這種模式,不再是很多人印象中那種毫無紀律一窩蜂的搶奪,latch使用一種隊列機制。
這個隊列的四個特性:
1.FIFO先進先出
2.可插隊
3.隊列中的latch請求每次隻能有一個(第一個)被外部喚醒。隊列中的latch請求不會自動醒來。
4.這個隊列并不是必定存在的。
當有一個latch正在被以exclusive模式獲得時,
此後繼續來的對此latch申請分兩種情況:
1.如果申請的也是exclusive模式,那麼cpu會自旋一定次數(注意,我這裡沒說是_spin_count次)不斷嘗試擷取,如果
還是沒能擷取,那麼将進入隊列中等待。(如果一定次數内能擷取,就不用進入隊列,是以說這個隊列并不是必定存在的)
2.如果申請是shared latch,發現已經被exclusive持有了,那麼會一次都不自旋的立刻進入隊列之中睡眠。
如果現在這個latch一直不被釋放,那麼後面隊列中的申請永遠都不會自動醒來。隻有,當一個hold的latch被釋放時候,會發送一個喚醒信号給隊列,僅喚醒隊列中的第一個睡眠的等待。
舉個例子,比如,現在一個execute模式的latch在hold,後面接着依次來了5個程序希望獲得share latch,發現latch
被以exclusive獲得,那麼将一次都不自旋的依次進入隊列等待。現在這個隊列是這樣的:
1s-2s-3s-4s-5s,
那麼當excluse模式的latch釋放後,他會發個信号給等待隊列,等待隊列中的第一個latch會被喚醒并且去獲得shared latch。
是以,這個隊列就變成了:
2s-3s-4s-5s!
你會發現,雖然都是share模式的latch!但是并沒有一次性被全部喚醒!
是以如果你經常做system的dump,你可能看到過這種情況,那就是一個程序在申請獲得以shared方式獲得一個latch,
你去檢查那個latch,發現這個latch是在以share模式持有的!
是以有了share waiting share!這還不是最神奇的,更神奇的還在後面。
這種latch的隊列,是允許插隊的。也就是說,譬如現在latch正在被share持有,而等待隊列是:
2s-3s-4s-5s
現在來了第6個share模式的申請,那麼他并不會進入等待隊列中,而是會直接獲得latch,因為share與share是相容的!
我們來說說更神奇的事。在9i的時候,這種方式還有很多bug。
不知道你有沒遇到過,一個查詢莫名的hang住,或者資料庫hang住。如果去做個trace,你可能會發現,程序正在等待獲得一個latch,而當你再去檢查那個latch時居然發現,那個latch并沒有被任何程序持有!是free的!也就是說,latch request waiting for a free latch!
這種bug的原因就是因為這個latch request處在上面所說的隊列中,并且由于某些原因,目前面latch釋放時并沒有将它喚醒,而它也絕對不會自己醒來。
如果你去找Oracle開SR,Oracle會讓你用這個隐藏參數解決:
_enable_reliable_latch_waits=false.
reliable--依賴。現在應該能看懂這個隐藏參數的意思了吧。
......(未完待續).......
看完覺得有點幫助的話幫忙頂下,您的支援,我的動力