OracleLeastRecentlyUsedChains

LRU Chains(or LRU lists)有它們相關(guān)的算法在過(guò)去已經(jīng)修改過(guò)多次。盡管算法已經(jīng)修改過(guò),但LRU chain的功能仍然相同:為了幫助被頻繁訪問(wèn)的buffer內(nèi)置在cache中和幫助服務(wù)器進(jìn)程快速地找到可被替換的buffers。任何時(shí)候單個(gè)列表都要努力地完成這兩個(gè)任務(wù),這將可能出現(xiàn)一些妥協(xié)。LRU chain也不例外,正如你將要發(fā)現(xiàn)的一樣,Oracle當(dāng)前的LRU算法實(shí)現(xiàn)的非常好,支持buffer caches超過(guò)100G的大小來(lái)滿足電信與政府系統(tǒng)的高事務(wù)處理的要求。

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到梅縣網(wǎng)站設(shè)計(jì)與梅縣網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、成都外貿(mào)網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名與空間、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋梅縣地區(qū)。

在Oracle 6中,只有單個(gè)LRU chain被單個(gè)LRU chain latch保護(hù)著。在大型的OLTP系統(tǒng)中,DBA將與LRU chainlatch競(jìng)爭(zhēng)進(jìn)行斗爭(zhēng)。但從Oracle 7開(kāi)始,Oracle通過(guò)將單個(gè)LRU chain分割成多個(gè)小的LRU chains,每個(gè)都有一個(gè)相關(guān)的LRU chain latch來(lái)緩解這種問(wèn)題。每個(gè)cache buffer存放在CBC結(jié)構(gòu)中并存放在一個(gè)LRU chain或一個(gè)寫(xiě)列表(也叫臟列表)中。buffers不會(huì)同時(shí)存放在一個(gè)寫(xiě)列表與一個(gè)LRU列表中。LRU chains要比CBCs長(zhǎng)太多。

臟buffers存放在一個(gè)LRU chain中不是問(wèn)題。事實(shí)上,如果臟buffers不能存放在一個(gè)LRU chain上將會(huì)影響性能。LRU chains的一目標(biāo)就是將被頻繁訪問(wèn)的buffers保留在cache中,并且許多臟buffers也會(huì)被頻繁地訪問(wèn)。當(dāng)在數(shù)據(jù)庫(kù)檢查點(diǎn)期間,每個(gè)臟buffer將被寫(xiě)入磁盤(pán)并再次變?yōu)閒ree buffer。

隱含參數(shù)_db_block_lru_latches顯示實(shí)例正在使用的的LRU chains的數(shù)量。與CBCs一樣,每個(gè)LRU chain latch控制著一組LRU chains的序列化。
SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
2 from x$ksppi x, x$ksppcv y
3 where x.inst_id=USERENV('Instance')
4 and y.inst_id=USERENV('Instance')
5 and x.indx=y.indx
6 and x.ksppinm like '%&par%';
Enter value for par: _db_block_lru_latches
old 6: and x.ksppinm like '%&par%'
new 6: and x.ksppinm like '%_db_block_lru_latches%'

NAME VALUE DESCRIB
---------------------- ------ --------------------------------
_db_block_lru_latches 640 number of lru latches

LRU Chain隨著時(shí)間的推移而變化
當(dāng)前的LRU chain算法被叫做touch-count算法,它使用計(jì)算頻率方案在每個(gè)buffer header上設(shè)置一個(gè)數(shù)字。但是Oracle花了很多年才實(shí)現(xiàn)這個(gè)算法。理解Oracle的LRU算法的發(fā)展更能了解LRU chains是如何工作的,它的缺點(diǎn)是什么以及如何確保它們按需執(zhí)行。

當(dāng)LRU chains出現(xiàn)性能問(wèn)題時(shí),大量的LRU chain latch競(jìng)爭(zhēng)將會(huì)出現(xiàn)。從Oracle算法角度來(lái)說(shuō),latch問(wèn)題通常會(huì)造成服務(wù)器進(jìn)程在搜索一個(gè)free buffer時(shí)持有一個(gè)lRU chain latch的時(shí)間太長(zhǎng)。這里存在許多相互關(guān)聯(lián)的原因,其解決方案也是一樣。

Standard LRU Algorithm(標(biāo)準(zhǔn)LRU算法)
不管Oracle的LRU算法如何,每個(gè)Oracle LRU chain有一個(gè)最近最少使用(LRU)端,也有一個(gè)最近頻繁使用(MRU)端?;\統(tǒng)地說(shuō),被頻繁訪問(wèn)的buffer header將存放在靠近MRU端,并且不被頻繁訪問(wèn)的buffer將存放在靠近LRU端。

標(biāo)準(zhǔn)LRU算法是非常簡(jiǎn)單的。當(dāng)一個(gè)buffer被放入cache中或被訪問(wèn)時(shí)(查詢或DML操作),buffer將被存放在會(huì)話相關(guān)的LRU chain(每個(gè)會(huì)話與一個(gè)LRU chain相關(guān))的MRU端。這種想法是一個(gè)被頻繁訪問(wèn)的buffer將被重復(fù)touched并且會(huì)被重復(fù)移動(dòng)到LRU chain中的MRU端。buffer移動(dòng)到LRU chain的MRU端通常叫做buffer promotion。如果一個(gè)buffer不被頻繁訪問(wèn),那么其它的buffer將被promoted或插入到LRU chain中,不被頻繁訪問(wèn)的buffer將被移動(dòng)到LRU chain的LRU端。

靠近每個(gè)LRU chain的LRU端可能潛伏著一個(gè)服務(wù)器進(jìn)程用來(lái)查找一個(gè)可用的不被頻繁訪問(wèn)的buffer好讓剛剛從磁盤(pán)中讀取來(lái)的塊替換掉它。假設(shè)LRU chain是8個(gè)buffer header那么長(zhǎng),全表掃描會(huì)掃描8個(gè)數(shù)據(jù)塊,并且每個(gè)數(shù)據(jù)塊將讀入Oracle cache中并且buffer headers會(huì)被放入LRU chain中。當(dāng)標(biāo)準(zhǔn)LRU算法使用時(shí),只有一個(gè)LRU chain,因此整個(gè)LRU鏈將被全表掃描所訪問(wèn)的數(shù)據(jù)塊所替換。隨著時(shí)間的推移包含被頻繁訪問(wèn)的buffer已經(jīng)被替換了。用戶肯定會(huì)注意到性能的變化,并且IO子系統(tǒng)也將受到打擊。當(dāng)數(shù)據(jù)庫(kù)大小繼續(xù)增長(zhǎng)的時(shí)候,Oracle顯然不得不進(jìn)行改進(jìn),所以修改了LRU算法。

Modified LRU Algorithm(修改后的LRU算法)
Oracle著名的LRU算法修改是在Oracle 6中。它是一次重大成就并且Oracle開(kāi)發(fā)者確實(shí)應(yīng)該對(duì)他們的高級(jí)buffer cache算法感到自豪。在這之后,它確實(shí)解決了標(biāo)準(zhǔn)LRU算法的關(guān)鍵問(wèn)題。

修改后的LRU算法與標(biāo)準(zhǔn)LRU算法僅有的區(qū)別是對(duì)LRU chain的LRU端的幾個(gè)buffer創(chuàng)建了一個(gè)窗口(用來(lái)存放被頻繁訪問(wèn)的buffers)。這個(gè)窗口的大小只有幾個(gè)buffers(例如,4個(gè))并且可以通過(guò)隱含參數(shù)_small_table_threshold來(lái)進(jìn)行修改。這可以確保不管對(duì)多大的表進(jìn)行全表掃描都將不會(huì)對(duì)cache產(chǎn)生什么影響。

Oracle修改后的LRU算法對(duì)一些buffer headers創(chuàng)建了一個(gè)窗口,當(dāng)所有全表掃描(FTS)的buffer headers被讀入到buffer cache時(shí)會(huì)經(jīng)過(guò)這個(gè)窗口。這確保了放在LRU chain中的MRU端的被頻繁訪問(wèn)的buffers不會(huì)被替換掉。

與其它所有算法一樣,修改的LRU算法也有限制,但這么多年來(lái)這些限制沒(méi)有造成問(wèn)題。然而,一旦客戶開(kāi)始使用Oracle來(lái)開(kāi)發(fā)大型數(shù)據(jù)倉(cāng)庫(kù)應(yīng)用程序時(shí),兩個(gè)顯著的問(wèn)題會(huì)出現(xiàn):
.大型數(shù)據(jù)倉(cāng)庫(kù)有大量的索引,并且當(dāng)大量索引使用范圍掃描時(shí),成千上萬(wàn)的索引葉子塊必須被讀入cache中。這個(gè)問(wèn)題直到Oracle 8i,如果索引葉子塊不在buffer cache中,Oracle將產(chǎn)生一個(gè)單塊IO請(qǐng)求(db file sequential read)將數(shù)據(jù)塊放入buffer cache。令人吃驚的是因?yàn)檫@不是一個(gè)多塊IO請(qǐng)求,索引buffer被插入到LRU chain的MRU端,這破壞了開(kāi)發(fā)良好的cache,現(xiàn)在完全存放著索引葉子塊buffers。

.當(dāng)數(shù)據(jù)塊被請(qǐng)求時(shí)(基于索引葉子塊),它們也會(huì)從IO子系統(tǒng)中(db file sequential read)被請(qǐng)求一次,因此再一次這些數(shù)據(jù)塊被放入到LRU chain中的MRU端。當(dāng)Oracle系統(tǒng)大小增加時(shí),Oracle的buffer cache減少了使用性。

Oracle's Touch-Count Algorithm
在Oracle 8.1.5中Oracle引入了一種完全修改好的LRU chain算法已經(jīng)完全消除了所有LRU chain latch競(jìng)爭(zhēng)問(wèn)題。關(guān)于這種修改沒(méi)有任何文檔記錄。發(fā)現(xiàn)算法改變是因?yàn)榭吹搅诵碌碾[含參數(shù)_db_percent_hot_default 和_db_aging_cool_count。當(dāng)有新的參數(shù)出現(xiàn)或有舊的參數(shù)丟棄時(shí),算法肯定有被修改。Oracle確實(shí)實(shí)現(xiàn)了計(jì)算機(jī)科學(xué)領(lǐng)域中通常所說(shuō)的計(jì)數(shù)頻率方案。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_percent_hot_default','_db_aging_cool_count');
NAME                          VALUE      DESCRIB
----------------------------- ---------- ------------------------------------------------
_db_percent_hot_default       50         Percent of default buffer pool considered hot
_db_aging_cool_count          1          Touch count set when buffer cooled

正如你所期待的,通用方法就是每次觸及buffer header時(shí)遞增計(jì)數(shù)器。更頻繁訪問(wèn)的buffer headers將有更高的觸及計(jì)數(shù)并且確實(shí)訪問(wèn)更頻繁,因此buffer將被保留在buffer cache。Oracle's touch-count算法判斷buffer header是否被頻繁訪問(wèn)是基于buffer header被觸及的次數(shù)來(lái)確定的。注意FTS(全表掃描)窗口的概念將不再需要并且已經(jīng)被刪除了。touch-count算法有三個(gè)關(guān)鍵點(diǎn):midpoint-insertion,touch count incrementation與buffer promotion

Midpoint Insertion
與修改后的LRU算法最根本的背離是midpoint insertion。每個(gè)LRU chain被分成hot區(qū)與cold區(qū)。當(dāng)一個(gè)buffer從磁盤(pán)被讀入且找到了一個(gè)free buffer,這個(gè)buffer與buffer header將替換之前的buffer與buffer header的內(nèi)容然后這個(gè)buffer header被移動(dòng)到LRU chain的midpoint。單塊讀,多塊讀,快速完全索引掃描或全表掃描都沒(méi)有差別。buffer header不會(huì)被插入到LRU chain的MRU端,而是LRU chain的midpoint。這確保了不會(huì)因?yàn)閱蝹€(gè)對(duì)象的大量數(shù)據(jù)塊被讀入到buffer cache中而使用LRU chain被破壞掉。

缺省情況下,hot區(qū)與cold區(qū)各占一半。midpoint確實(shí)在中間。然而這個(gè)可以通過(guò)隱含參數(shù)_db_percent_hot_default來(lái)配置。

當(dāng)其它buffer headers被插入到midpoint或被promoted(提升)時(shí),原有的buffer headers自然地將從LRU chain的hot區(qū)移動(dòng)到cold區(qū)。在一個(gè)buffer header被插入后,只有一種方式可以保留在cache很長(zhǎng)時(shí)間就是被不斷重復(fù)地promoted。

因?yàn)榇翱诜桨赣糜谛薷牡腖RU算法中而不再被使用,隱含參數(shù)_small_table_threshold因此被丟棄。然而在Oracle11g中,它又再次被使用,但是用于不同的目的。從Oracle 11g開(kāi)始,_small_table_threshold參數(shù)是服務(wù)器進(jìn)程開(kāi)始執(zhí)行直接路徑讀的閾值。直接路徑讀可以提高性能因?yàn)閿?shù)據(jù)塊從磁盤(pán)直接讀取到服務(wù)器進(jìn)程的PGA內(nèi)存中而不用放入buffer cache。然而,這是更自私的讀取操作并且可能實(shí)際上降低性能,因?yàn)槠渌姆?wù)器進(jìn)程不能從IO操作中獲利。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm like '%&par%';
Enter value for par: _small_table_threshold
old   6: and x.ksppinm like '%&par%'
new   6: and x.ksppinm like '%_small_table_threshold%'
NAME                           VALUE                           DESCRIB
------------------------------ ------------------------------  -----------------------------------------------------
_small_table_threshold         60283                           lower threshold level of table size for direct reads

假設(shè)你是一個(gè)服務(wù)器進(jìn)程必須要查詢一行存放在特定數(shù)據(jù)塊中的記錄。基于這個(gè)SQL語(yǔ)句與數(shù)據(jù)字典,你知道數(shù)據(jù)塊的文件號(hào)與塊號(hào)。如果只關(guān)心查詢速度,因此希望這個(gè)數(shù)據(jù)塊已經(jīng)存放在buffer cache中了。為了檢查數(shù)據(jù)塊是否存放在buffer cache中,需要得到buffer's buffer cache內(nèi)存地址,它存放在它的buffer header中。

為了找到buffer header,必須訪問(wèn)CBC結(jié)構(gòu)。哈希文件號(hào)與塊號(hào),它將指向一個(gè)哈希桶?;谶@個(gè)哈希桶,可以查找相關(guān)的CBC latch與持有它。在幾次spin后,你可能可以獲得latch,因此開(kāi)始你的序列化CBC搜索。第一個(gè)buffer header如果不是你想要的,并且不幸地是在這個(gè)CBC中沒(méi)有第二個(gè)buffer header,因此知道buffer當(dāng)前沒(méi)有放入buffer cache。

釋放CBC latch并執(zhí)行調(diào)用給操作系統(tǒng),要求訪問(wèn)你需要的數(shù)據(jù)塊。當(dāng)你正等待時(shí),你將被告知db file sequential read等待事件。最終從操作系統(tǒng)接收到這個(gè)數(shù)據(jù)塊并在PGA中持有它。因?yàn)闆](méi)有使用直接路徑讀,在你或其它服務(wù)器進(jìn)程訪問(wèn)buffer之前,buffer必須被合理地插入到buffer cache并更新所有合理結(jié)構(gòu)。

你將需要一個(gè)free buffer用來(lái)在buffer cache中存放剛讀取的數(shù)據(jù)塊,因此你將移到LRU chain的LRU端。但在你開(kāi)始掃描LRU chain之前,你必須持有并獲得相關(guān)的LRU chain latch。之后當(dāng)休眠時(shí)通過(guò)spinning與posting等待事件latch:cache buffers lru chains來(lái)消耗CPU,最終獲得latch。從LRU chain的LRU端開(kāi)始,你查看buffer header是否它是一個(gè)不被頻繁訪問(wèn)的free buffer,得到的回答是它是不被頻繁訪問(wèn)的buffer。那么你現(xiàn)在就可開(kāi)始buffer替換操作。你立即pin(固定)住這個(gè)buffer header。從buffer header中,可以獲得數(shù)據(jù)塊對(duì)應(yīng)buffer在buffer cache中的內(nèi)存地址,使用剛被讀取的且仍在你PGA內(nèi)存中的塊來(lái)替換這個(gè)free buffer,執(zhí)行任何要求buffer header所要進(jìn)行的修改。你維護(hù)這個(gè)LRU chain并移動(dòng)buffer header到LRU chain's midpoint,釋放LRU chian latch,并unpin這個(gè)buffer header。現(xiàn)在任何服務(wù)器或后臺(tái)進(jìn)程包括你可以訪問(wèn)這個(gè)buffer,這將都是在一瞬間就能完成。

Touch Count Incrementation
這個(gè)概念是一個(gè)buffer header每被touch一次,它的touch count將會(huì)增加。事實(shí)上并不是這樣。缺省情況下,一個(gè)buffer header的touch count只有每3秒才會(huì)增加一次。這可以用來(lái)確保buffer活動(dòng)時(shí)間超過(guò)幾秒才算做被頻繁訪問(wèn)

當(dāng)一個(gè)buffer被插入到buffer cache中時(shí),它的touch count被設(shè)置為0.然而,如果buffer在短期內(nèi)被重復(fù)地touch,那么touch將不會(huì)進(jìn)行計(jì)數(shù)。

Oracle也允許touch count被遺漏。這將沒(méi)有l(wèi)atch被調(diào)用(這是消除latch競(jìng)爭(zhēng)最好的方法),并且Oracle不會(huì)pin住buffer header。不使用序列化控制,兩個(gè)服務(wù)器進(jìn)程可以遞增與更新buffer header's的touch count到相同的值。

假設(shè)服務(wù)器進(jìn)程S100在時(shí)間T0點(diǎn)得到的buffer header的touch count是13,并且開(kāi)始遞增為14。但服務(wù)器進(jìn)程S200現(xiàn)在在時(shí)間T1點(diǎn)詢問(wèn)這個(gè)buffer header的touch count,并且因?yàn)榉?wù)器進(jìn)程S100還沒(méi)有完成touch count的遞增操作,所以buffer header的touch count現(xiàn)在仍然顯示為13。服務(wù)器進(jìn)程S200現(xiàn)在開(kāi)始將touch count從13遞增到14。在時(shí)間T2點(diǎn),服務(wù)器進(jìn)程S100將buffer header的touch count修改為14,并且在時(shí)間T3點(diǎn),服務(wù)器進(jìn)程S200也將buffer header的touch count修改為14。這是不是touch count遞增被遺漏了?沒(méi)有結(jié)構(gòu)被損壞,并且touch count確實(shí)已經(jīng)被遞增了,但不是遞增兩次。如果一個(gè)buffer確實(shí)被頻繁地訪問(wèn),它將再次被touch。通過(guò)這種模糊實(shí)現(xiàn)節(jié)省的是CPU的消耗與內(nèi)核代碼運(yùn)行量。

Buffer Promotion
沒(méi)有說(shuō)當(dāng)一個(gè)buffer被touch后,它將會(huì)被promoted到LRU chain的MRU端。這是因?yàn)閎uffer header的touching與buffer header的promotion現(xiàn)在是兩個(gè)分開(kāi)的操作。當(dāng)一個(gè)buffer被考慮進(jìn)行promotion時(shí),也會(huì)考慮替換它。而服務(wù)器進(jìn)程與數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程都可以promote buffer header,但只有一個(gè)服務(wù)器進(jìn)程將替換這個(gè)buffer并且與它相關(guān)的buffer header作為一個(gè)物理讀取數(shù)據(jù)塊的結(jié)果。數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程執(zhí)行替換沒(méi)有意義,因?yàn)樗鼪](méi)有替換的內(nèi)容。

在一個(gè)服務(wù)器進(jìn)程從磁盤(pán)讀取一個(gè)數(shù)據(jù)塊之后,它必須要找到一個(gè)不被頻繁訪問(wèn)的free buffer來(lái)存放剛被讀取的數(shù)據(jù)塊。服務(wù)器進(jìn)程要獲得適當(dāng)?shù)腖RU latch,然后從LRU chain的LRU端開(kāi)始掃描buffer headers。記住buffer headers存放在LRU chain中,不是buffers中。如果服務(wù)器進(jìn)程遇到了一個(gè)free buffer header,那么它檢查它是否被頻繁訪問(wèn)。如果被頻繁訪問(wèn),服務(wù)器進(jìn)程將promote這個(gè)buffer header,然后繼續(xù)掃描。如果這個(gè)free buffer header不被頻繁訪問(wèn),服務(wù)器進(jìn)程將使用從磁盤(pán)讀取到的數(shù)據(jù)塊來(lái)替換這個(gè)buffer,并更新buffer header,移動(dòng)buffer header到LRU chain的midpoint。注意這里不需要更新CBC結(jié)構(gòu),因?yàn)閎uffer沒(méi)有被移動(dòng),只有LRU chain上的buffer header被移動(dòng)。如果服務(wù)器進(jìn)程遇到一個(gè)dirty buffer header,那么檢查是否是一個(gè)被頻繁訪問(wèn)的dirty buffer header。如果dirty buffer header被頻繁訪問(wèn),它將promote這個(gè)buffer header并繼續(xù)掃描。如果dirty buffer header不被頻繁訪問(wèn),服務(wù)器進(jìn)程將移動(dòng)這個(gè)buffer header到寫(xiě)列表中。如果服務(wù)器進(jìn)程遇到一個(gè)被pin 住的buffer header,那將繼續(xù)掃描。pin住的buffer被禁止使用。

promotion操作只要達(dá)到最低值2(_db_aging_hot_criteria)就會(huì)中斷。因此當(dāng)一個(gè)服務(wù)器進(jìn)程或數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程在詢問(wèn)“每個(gè)buffer的touch count數(shù)是多少?"時(shí),它實(shí)際是問(wèn)“buffer的touch count是否大于或等于_db_aging_hot_criteria?”。如果每隔幾秒一個(gè)buffer就會(huì)被touch,那么它應(yīng)該被保留在cache中。如果不是,它將被快速替換掉。

當(dāng)一個(gè)被頻繁訪問(wèn)的buffer被promoted時(shí),它的生命周期將變得更困難。promotion操作的一部分是touch count被設(shè)置為0(_db_aging_stay_count)。除非buffer是一個(gè)segment header或一個(gè)consistent read(CR) buffer,否則會(huì)出現(xiàn)這種情況。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_aging_stay_count');
NAME                      VALUE        DESCRIB
------------------------- ------------ --------------------------------------------------------------
_db_aging_stay_count      0            Touch count set when buffer moved to head of replacement list

數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程也可能promote被頻繁訪問(wèn)的buffer headers。當(dāng)一個(gè)數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程處于休眠狀態(tài),它將每3秒鐘被喚醒一次。每個(gè)數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程都有一個(gè)屬于它的寫(xiě)列表(dirty列表)并且它也與一個(gè)或多個(gè)LRU chain相關(guān)聯(lián)。當(dāng)一個(gè)LRU chain的數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程被喚醒,它將檢查它的寫(xiě)列表來(lái)查看寫(xiě)列表的長(zhǎng)度是否足夠執(zhí)行一個(gè)IO寫(xiě)操作。如果數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程決定構(gòu)建一個(gè)寫(xiě)列表,它將掃描它的LRU chain來(lái)查找不被頻繁訪問(wèn)的dirty buffer。非常像服務(wù)器進(jìn)程查找free buffer那樣,數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程也將獲得相關(guān)的LRU chain lath,從LRU chain的LRU端開(kāi)始并檢查buffer header是否為dirty且不被頻繁訪問(wèn)。如果一個(gè)不被頻繁訪問(wèn)的dirty buffer被找到,數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程將會(huì)這個(gè)buffer header從LRU chain移動(dòng)到它的寫(xiě)列表中(記住,這個(gè)buffer header仍然存放在CBC結(jié)構(gòu)中,因此它能被其它進(jìn)程找到)。如果寫(xiě)列表的長(zhǎng)度仍然不足夠執(zhí)行一次IO寫(xiě)操作,那么數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程將繼續(xù)掃描它的LRU chain,查找更多的不被頻繁訪問(wèn)的dirty buffer headers。

Hot Region to Cold Region Movement
一個(gè)buffer header的生命周期在LRU chain是從midpoint(正中間)開(kāi)始的。因?yàn)槠渌黚uffer headers將被替換并且被插入到正中間,隨著buffers被promoted,一個(gè)buffer header自然地將遷移到LRU chain的LRU端。promote一個(gè)buffer header的唯一方法就是buffer header標(biāo)識(shí)為被頻繁訪問(wèn)。當(dāng)一個(gè)buffer跨過(guò)正中間(midpoint)時(shí)另一個(gè)顯著事件會(huì)出現(xiàn),那就是從hot region移動(dòng)到cold region。

當(dāng)一個(gè)buffer進(jìn)入到cold region中時(shí),它的touch count會(huì)被重設(shè)置為缺省值1(_db_aging_cool_count)。這有冷卻hot buffer的效果,任何希望保留在cache中的buffer都不想出現(xiàn)這種情況。增加這個(gè)參數(shù)值將人為增加buffer值從而增加了buffer移動(dòng)的可能性。因此缺省情況下,當(dāng)一個(gè)buffer header進(jìn)行到cold region時(shí),它必須至少被touched一次來(lái)使其匹配promotion操作的條件(_db_aging_hot_criteria)。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_aging_cool_count');
NAME                      VALUE     DESCRIB
------------------------- --------- ------------------------------------
_db_aging_cool_count      1         Touch count set when buffer cooled
SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_aging_hot_criteria');
NAME                       VALUE      DESCRIB
-------------------------- ---------- ---------------------------------------------------------------
_db_aging_hot_criteria     2          Touch count which sends a buffer to head of replacement list

Touch Count Changes
可能會(huì)疑問(wèn)為什么當(dāng)一個(gè)buffer header被promoted和當(dāng)它進(jìn)入到cold region時(shí)Oracle要重新設(shè)置touch count。要理解這一點(diǎn)關(guān)鍵要理解中間點(diǎn)(midpoint)。中間點(diǎn)(midpoint)缺省情況下將每個(gè)LRU chain平分為hot與cold
region(_db_percent_hot_default=50),它可以被設(shè)置為0到100之間的任何數(shù)值。如果LRU chain變成一個(gè)100%的hot region,那么唯一的touch count重置將發(fā)生在buffer被promoted時(shí)。當(dāng)Oracle釋放出創(chuàng)建任何數(shù)量buffer pools的能力時(shí),在每個(gè)pool中維護(hù)中間點(diǎn)(midpoint)的能力將允許高度優(yōu)化和特定的LRU活動(dòng)。盡管雙重設(shè)置可能最初看起來(lái)比較愚蠢,但它確實(shí)有其真正的目的并為將來(lái)奠定了基礎(chǔ)。

SQL> select '00 : '||count(*) x from x$bh where tch=0
  2  union
  3  select '01 : '||count(*) x from x$bh where tch=1
  4  union
  5  select '02 : '||count(*) x from x$bh where tch=2
  6  union
  7  select '03 : '||count(*) x from x$bh where tch=3
  8  union
  9  select '04 : '||count(*) x from x$bh where tch=4
 10  union
 11  select '05 : '||count(*) x from x$bh where tch=5
 12  union
 13  select '06 : '||count(*) x from x$bh where tch=6
 14  union
 15  select '07 : '||count(*) x from x$bh where tch=7
 16  union
 17  select '08 : '||count(*) x from x$bh where tch=8
 18  union
 19  select '09 : '||count(*) x from x$bh where tch=9
 20  union
 21  select '10 : '||count(*) x from x$bh where tch=10
 22  union
 23  select '11 : '||count(*) x from x$bh where tch=11
 24  union
 25  select '12 : '||count(*) x from x$bh where tch=12
 26  union
 27  select '13 : '||count(*) x from x$bh where tch=13
 28  union
 29  select '14 : '||count(*) x from x$bh where tch=14
 30  union
 31  select '15 : '||count(*) x from x$bh where tch=15
 32  union
 33  select '16 : '||count(*) x from x$bh where tch=16
 34  /
X
---------------------------------------------
00 : 1879125
01 : 697463
02 : 254482
03 : 227324
04 : 161410
05 : 141651
06 : 91699
07 : 70599
08 : 55605
09 : 25551
10 : 17181
11 : 29833
12 : 19978
13 : 13324
14 : 29006
15 : 9998
16 : 9649
17 rows selected

touch count被重新設(shè)置有重要影響。首先,這意味著touch count不會(huì)飆升到無(wú)窮大。touch count重新設(shè)置也意味著最被頻繁訪問(wèn)的buffer headers將不需要有最高的touch counts。如果你注意到一個(gè)特定的buffer有一個(gè)較低的touch count,那么你可能捕獲了一個(gè)被頻繁訪問(wèn)的buffer,只是它可能剛剛被promoted或進(jìn)入到LRU chain的cold region。事實(shí)上,最高touch count的buffer headers將存放在LRU chain的LRU端附近。

LRU Chain Contention Identification and Resolution
Oracle的LRU touch-count算法,與缺省的實(shí)例參數(shù)設(shè)置進(jìn)行組合來(lái)使用微不足道的競(jìng)爭(zhēng)來(lái)啟用高性能LRU chain活動(dòng)。當(dāng)touch-count算法遇到壓力時(shí),這是IO和CPU活動(dòng)的獨(dú)特組合。

LRU chain latches命名為cache buffers lru chain。哈希chain latches被命名為cache buffer chains。命名很接近并且可能導(dǎo)致相當(dāng)大的混亂。只要記住LRU chain latches的名字中l(wèi)ru就不會(huì)混亂。在Oracle 10g之前的版本中,等待事件被簡(jiǎn)化成latch free,為了判斷特定的latch,需要使用v$session_wait視圖中的p2列與v$latch中的latch#進(jìn)行關(guān)聯(lián)來(lái)進(jìn)行查詢。對(duì)于Oracle 10g及以后的版本,等待事件標(biāo)識(shí)為latch:cache buffers lru chain。

如果不需要執(zhí)行物理讀來(lái)從磁盤(pán)讀取數(shù)據(jù),那么就不會(huì)存在LRU chain latch競(jìng)爭(zhēng),因?yàn)榫筒恍枰檎襢ree buffer或者插入一個(gè)buffer header到一個(gè)LRU chain中。數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程查找不被頻繁訪問(wèn)的dirty buffers不會(huì)對(duì)LRU chain結(jié)構(gòu)造成壓力從而導(dǎo)致LRU chain latch的競(jìng)爭(zhēng)。然而,任何時(shí)候一個(gè)服務(wù)器進(jìn)程從磁盤(pán)讀取數(shù)據(jù)塊,它必須要找到一個(gè)free buffer,這將請(qǐng)求LRU chain活動(dòng)(除了直接路徑讀)。如果IO讀區(qū)花了10ms,那么你可能看到的是db file scattered read與db file sequential read等待事件而不是LRU chain latch競(jìng)爭(zhēng)。但如果IO子系統(tǒng)返回?cái)?shù)據(jù)塊的時(shí)間少于5ms,那么壓力就轉(zhuǎn)移到CPU子系統(tǒng)了,并且這時(shí)LRU chain的活動(dòng)將開(kāi)始承受壓力。

LRU chain latch競(jìng)爭(zhēng)可能的結(jié)果是獲取latch的問(wèn)題,持有l(wèi)atch大長(zhǎng)時(shí)間或者兩個(gè)同時(shí)出現(xiàn)。如果操作系統(tǒng)的CPU受限,獲得latch可能花費(fèi)很長(zhǎng)時(shí)間,因?yàn)闆](méi)有足夠的CPU周期。一旦latch被獲得且LRU chain相關(guān)的內(nèi)核代碼被運(yùn)行,如果CPU周期供應(yīng)不足或者不被頻繁訪問(wèn)的free buffers有限,LRU chain latch可能被持有很長(zhǎng)時(shí)間足夠造成嚴(yán)重的競(jìng)爭(zhēng)。

因此,首先,必須要有強(qiáng)烈的物理讀取活動(dòng)。第二,IO子系統(tǒng)響應(yīng)時(shí)間非??欤瑢⒋蟛糠值牡却龝r(shí)間從讀取等待事件傳遞到LRU chain latch等待事件。這種競(jìng)爭(zhēng)提供了許多可供組合使用解決方法:
.優(yōu)化物理IO SQL語(yǔ)句
如果沒(méi)有物理IO存在就不會(huì)有大量的LRU chain latch競(jìng)爭(zhēng)。因此,從應(yīng)用程序角度來(lái)說(shuō)產(chǎn),查找主要活動(dòng)為執(zhí)行物理塊讀取也就是物理IO活動(dòng)的SQL語(yǔ)句。盡你所能地減少SQL語(yǔ)句的物理IO消耗。這意味著執(zhí)行經(jīng)典的SQL優(yōu)化操作,包括使用索引,以及在性能關(guān)鍵時(shí)期減少頂級(jí)物理IO SQL語(yǔ)句的執(zhí)行速度。

.增加CPU處理能力
與CBC latch競(jìng)爭(zhēng)一樣或任何其它latch競(jìng)爭(zhēng)一樣,如果有更多的CPU資源可以使用,內(nèi)存管理將會(huì)花費(fèi)更少的時(shí)間。這意味著latch持有時(shí)間與latch獲取時(shí)間(spinning與sleeping)將被減少。增加CPU處理能力也意味著在競(jìng)爭(zhēng)高峰期間尋找創(chuàng)建性方法來(lái)減秒CPU消耗。

.增加LRU latch數(shù)量
通過(guò)增加latches可以增加LRU的并發(fā),這意味著增加隱含參數(shù)_db_block_lru_latches的值。如果有很多G的buffer cache增加latches可能是特別有效的。

.使用多個(gè)buffer pools
一種創(chuàng)造性策略來(lái)減少主LRU chain壓力的方法就是實(shí)現(xiàn)keep與recycle pools。所有的buffer pools都可以增加LRUchain latches的數(shù)量。它們也使用touch-count算未能,并且有類似的touch count實(shí)例參數(shù),比如_db_percent_hot_keep

.調(diào)用touch count實(shí)例參數(shù)
有幾個(gè)可用touch count參數(shù)。但要注意,這些參數(shù)的值都很小,比如1和2。因引,即使參數(shù)從1修改為2都是相當(dāng)大的改變可能導(dǎo)致意想不到的后果。只有在測(cè)試后將調(diào)整touch count參數(shù)作為最后的手段。

_db_percent_hot_default參數(shù),它的缺省值為50。它表示在hot region的buffer headers的百分比。如果想要更多的buffer header存放在hot region,可以增加這個(gè)參數(shù)。減小這個(gè)參數(shù)將會(huì)給予buffer headers在遇到一個(gè)服務(wù)器進(jìn)程或數(shù)據(jù)庫(kù)寫(xiě)進(jìn)程之前更多的時(shí)間來(lái)被touched。

_db_aging_touch_time參數(shù),它的缺省值為3它是唯一能增加一個(gè)buffer header的touch count(x$bh.tch)時(shí)間窗口的方法。增加這個(gè)參數(shù)將減小突然爆發(fā)以buffer為中心活動(dòng)的影響,同時(shí)會(huì)冒著貶值頻繁被訪問(wèn)buffer的風(fēng)險(xiǎn)。

_db_aging_hot_criteria參數(shù),它的缺省值為2。一個(gè)buffer header的touch count閾值必須滿足或被超過(guò)才能被promoted(提升)。如果想一個(gè)buffer被promoted更困難,可以增加這個(gè)參數(shù)值。那么只有真正hot buffers才會(huì)被保留在cache中。

_db_aging_stay_count參數(shù),它的缺省值為0。當(dāng)一個(gè)buffer header被promoted時(shí)touch count被重設(shè)置后的值。一致性讀與段頭塊除外。

_db_aging_cool_count參數(shù),它的缺省值為1。當(dāng)一個(gè)buffer header從hot region進(jìn)入cold region時(shí)touch count被重設(shè)置后的值。減小這個(gè)參數(shù)值將使buffer header被promoted變得更困難。

_db_aging_freeze_cr參數(shù),它的缺省值為false。使一致性讀取的 buffers總是為cold狀態(tài),因此它們?nèi)菀妆惶鎿Q。

網(wǎng)站欄目:OracleLeastRecentlyUsedChains
轉(zhuǎn)載源于:http://muchs.cn/article34/jchhse.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、外貿(mào)建站、域名注冊(cè)、虛擬主機(jī)網(wǎng)站改版、網(wǎng)站維護(hù)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)