您好,登錄后才能下訂單哦!
LRU Chains(or LRU lists)有它們相關(guān)的算法在過去已經(jīng)修改過多次。盡管算法已經(jīng)修改過,但LRU chain的功能仍然相同:為了幫助被頻繁訪問的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超過100G的大小來滿足電信與政府系統(tǒng)的高事務(wù)處理的要求。
在Oracle 6中,只有單個(gè)LRU chain被單個(gè)LRU chain latch保護(hù)著。在大型的OLTP系統(tǒng)中,DBA將與LRU chainlatch競爭進(jìn)行斗爭。但從Oracle 7開始,Oracle通過將單個(gè)LRU chain分割成多個(gè)小的LRU chains,每個(gè)都有一個(gè)相關(guān)的LRU chain latch來緩解這種問題。每個(gè)cache buffer存放在CBC結(jié)構(gòu)中并存放在一個(gè)LRU chain或一個(gè)寫列表(也叫臟列表)中。buffers不會(huì)同時(shí)存放在一個(gè)寫列表與一個(gè)LRU列表中。LRU chains要比CBCs長太多。
臟buffers存放在一個(gè)LRU chain中不是問題。事實(shí)上,如果臟buffers不能存放在一個(gè)LRU chain上將會(huì)影響性能。LRU chains的一目標(biāo)就是將被頻繁訪問的buffers保留在cache中,并且許多臟buffers也會(huì)被頻繁地訪問。當(dāng)在數(shù)據(jù)庫檢查點(diǎn)期間,每個(gè)臟buffer將被寫入磁盤并再次變?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)性能問題時(shí),大量的LRU chain latch競爭將會(huì)出現(xiàn)。從Oracle算法角度來說,latch問題通常會(huì)造成服務(wù)器進(jìn)程在搜索一個(gè)free buffer時(shí)持有一個(gè)lRU chain latch的時(shí)間太長。這里存在許多相互關(guān)聯(lián)的原因,其解決方案也是一樣。
Standard LRU Algorithm(標(biāo)準(zhǔn)LRU算法)
不管Oracle的LRU算法如何,每個(gè)Oracle LRU chain有一個(gè)最近最少使用(LRU)端,也有一個(gè)最近頻繁使用(MRU)端?;\統(tǒng)地說,被頻繁訪問的buffer header將存放在靠近MRU端,并且不被頻繁訪問的buffer將存放在靠近LRU端。
標(biāo)準(zhǔn)LRU算法是非常簡單的。當(dāng)一個(gè)buffer被放入cache中或被訪問時(shí)(查詢或DML操作),buffer將被存放在會(huì)話相關(guān)的LRU chain(每個(gè)會(huì)話與一個(gè)LRU chain相關(guān))的MRU端。這種想法是一個(gè)被頻繁訪問的buffer將被重復(fù)touched并且會(huì)被重復(fù)移動(dòng)到LRU chain中的MRU端。buffer移動(dòng)到LRU chain的MRU端通常叫做buffer promotion。如果一個(gè)buffer不被頻繁訪問,那么其它的buffer將被promoted或插入到LRU chain中,不被頻繁訪問的buffer將被移動(dòng)到LRU chain的LRU端。
靠近每個(gè)LRU chain的LRU端可能潛伏著一個(gè)服務(wù)器進(jìn)程用來查找一個(gè)可用的不被頻繁訪問的buffer好讓剛剛從磁盤中讀取來的塊替換掉它。假設(shè)LRU chain是8個(gè)buffer header那么長,全表掃描會(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鏈將被全表掃描所訪問的數(shù)據(jù)塊所替換。隨著時(shí)間的推移包含被頻繁訪問的buffer已經(jīng)被替換了。用戶肯定會(huì)注意到性能的變化,并且IO子系統(tǒng)也將受到打擊。當(dāng)數(shù)據(jù)庫大小繼續(xù)增長的時(shí)候,Oracle顯然不得不進(jìn)行改進(jìn),所以修改了LRU算法。
Modified LRU Algorithm(修改后的LRU算法)
Oracle著名的LRU算法修改是在Oracle 6中。它是一次重大成就并且Oracle開發(fā)者確實(shí)應(yīng)該對(duì)他們的高級(jí)buffer cache算法感到自豪。在這之后,它確實(shí)解決了標(biāo)準(zhǔn)LRU算法的關(guān)鍵問題。
修改后的LRU算法與標(biāo)準(zhǔn)LRU算法僅有的區(qū)別是對(duì)LRU chain的LRU端的幾個(gè)buffer創(chuàng)建了一個(gè)窗口(用來存放被頻繁訪問的buffers)。這個(gè)窗口的大小只有幾個(gè)buffers(例如,4個(gè))并且可以通過隱含參數(shù)_small_table_threshold來進(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)過這個(gè)窗口。這確保了放在LRU chain中的MRU端的被頻繁訪問的buffers不會(huì)被替換掉。
與其它所有算法一樣,修改的LRU算法也有限制,但這么多年來這些限制沒有造成問題。然而,一旦客戶開始使用Oracle來開發(fā)大型數(shù)據(jù)倉庫應(yīng)用程序時(shí),兩個(gè)顯著的問題會(huì)出現(xiàn):
.大型數(shù)據(jù)倉庫有大量的索引,并且當(dāng)大量索引使用范圍掃描時(shí),成千上萬的索引葉子塊必須被讀入cache中。這個(gè)問題直到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端,這破壞了開發(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競爭問題。關(guān)于這種修改沒有任何文檔記錄。發(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)域中通常所說的計(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ù)器。更頻繁訪問的buffer headers將有更高的觸及計(jì)數(shù)并且確實(shí)訪問更頻繁,因此buffer將被保留在buffer cache。Oracle's touch-count算法判斷buffer header是否被頻繁訪問是基于buffer header被觸及的次數(shù)來確定的。注意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從磁盤被讀入且找到了一個(gè)free buffer,這個(gè)buffer與buffer header將替換之前的buffer與buffer header的內(nèi)容然后這個(gè)buffer header被移動(dòng)到LRU chain的midpoint。單塊讀,多塊讀,快速完全索引掃描或全表掃描都沒有差別。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è)可以通過隱含參數(shù)_db_percent_hot_default來配置。
當(dāng)其它buffer headers被插入到midpoint或被promoted(提升)時(shí),原有的buffer headers自然地將從LRU chain的hot區(qū)移動(dòng)到cold區(qū)。在一個(gè)buffer header被插入后,只有一種方式可以保留在cache很長時(shí)間就是被不斷重復(fù)地promoted。
因?yàn)榇翱诜桨赣糜谛薷牡腖RU算法中而不再被使用,隱含參數(shù)_small_table_threshold因此被丟棄。然而在Oracle11g中,它又再次被使用,但是用于不同的目的。從Oracle 11g開始,_small_table_threshold參數(shù)是服務(wù)器進(jìn)程開始執(zhí)行直接路徑讀的閾值。直接路徑讀可以提高性能因?yàn)閿?shù)據(jù)塊從磁盤直接讀取到服務(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語句與數(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,必須訪問CBC結(jié)構(gòu)。哈希文件號(hào)與塊號(hào),它將指向一個(gè)哈希桶?;谶@個(gè)哈希桶,可以查找相關(guān)的CBC latch與持有它。在幾次spin后,你可能可以獲得latch,因此開始你的序列化CBC搜索。第一個(gè)buffer header如果不是你想要的,并且不幸地是在這個(gè)CBC中沒有第二個(gè)buffer header,因此知道buffer當(dāng)前沒有放入buffer cache。
釋放CBC latch并執(zhí)行調(diào)用給操作系統(tǒng),要求訪問你需要的數(shù)據(jù)塊。當(dāng)你正等待時(shí),你將被告知db file sequential read等待事件。最終從操作系統(tǒng)接收到這個(gè)數(shù)據(jù)塊并在PGA中持有它。因?yàn)闆]有使用直接路徑讀,在你或其它服務(wù)器進(jìn)程訪問buffer之前,buffer必須被合理地插入到buffer cache并更新所有合理結(jié)構(gòu)。
你將需要一個(gè)free buffer用來在buffer cache中存放剛讀取的數(shù)據(jù)塊,因此你將移到LRU chain的LRU端。但在你開始掃描LRU chain之前,你必須持有并獲得相關(guān)的LRU chain latch。之后當(dāng)休眠時(shí)通過spinning與posting等待事件latch:cache buffers lru chains來消耗CPU,最終獲得latch。從LRU chain的LRU端開始,你查看buffer header是否它是一個(gè)不被頻繁訪問的free buffer,得到的回答是它是不被頻繁訪問的buffer。那么你現(xiàn)在就可開始buffer替換操作。你立即pin(固定)住這個(gè)buffer header。從buffer header中,可以獲得數(shù)據(jù)塊對(duì)應(yīng)buffer在buffer cache中的內(nèi)存地址,使用剛被讀取的且仍在你PGA內(nè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?,F(xiàn)在任何服務(wù)器或后臺(tái)進(jìn)程包括你可以訪問這個(gè)buffer,這將都是在一瞬間就能完成。
Touch Count Incrementation
這個(gè)概念是一個(gè)buffer header每被touch一次,它的touch count將會(huì)增加。事實(shí)上并不是這樣。缺省情況下,一個(gè)buffer header的touch count只有每3秒才會(huì)增加一次。這可以用來確保buffer活動(dòng)時(shí)間超過幾秒才算做被頻繁訪問
當(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被遺漏。這將沒有l(wèi)atch被調(diào)用(這是消除latch競爭最好的方法),并且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,并且開始遞增為14。但服務(wù)器進(jìn)程S200現(xiàn)在在時(shí)間T1點(diǎn)詢問這個(gè)buffer header的touch count,并且因?yàn)榉?wù)器進(jìn)程S100還沒有完成touch count的遞增操作,所以buffer header的touch count現(xiàn)在仍然顯示為13。服務(wù)器進(jìn)程S200現(xiàn)在開始將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遞增被遺漏了?沒有結(jié)構(gòu)被損壞,并且touch count確實(shí)已經(jīng)被遞增了,但不是遞增兩次。如果一個(gè)buffer確實(shí)被頻繁地訪問,它將再次被touch。通過這種模糊實(shí)現(xiàn)節(jié)省的是CPU的消耗與內(nèi)核代碼運(yùn)行量。
Buffer Promotion
沒有說當(dāng)一個(gè)buffer被touch后,它將會(huì)被promoted到LRU chain的MRU端。這是因?yàn)閎uffer header的touching與buffer header的promotion現(xiàn)在是兩個(gè)分開的操作。當(dāng)一個(gè)buffer被考慮進(jìn)行promotion時(shí),也會(huì)考慮替換它。而服務(wù)器進(jìn)程與數(shù)據(jù)庫寫進(jìn)程都可以promote buffer header,但只有一個(gè)服務(wù)器進(jìn)程將替換這個(gè)buffer并且與它相關(guān)的buffer header作為一個(gè)物理讀取數(shù)據(jù)塊的結(jié)果。數(shù)據(jù)庫寫進(jìn)程執(zhí)行替換沒有意義,因?yàn)樗鼪]有替換的內(nèi)容。
在一個(gè)服務(wù)器進(jìn)程從磁盤讀取一個(gè)數(shù)據(jù)塊之后,它必須要找到一個(gè)不被頻繁訪問的free buffer來存放剛被讀取的數(shù)據(jù)塊。服務(wù)器進(jìn)程要獲得適當(dāng)?shù)腖RU latch,然后從LRU chain的LRU端開始掃描buffer headers。記住buffer headers存放在LRU chain中,不是buffers中。如果服務(wù)器進(jìn)程遇到了一個(gè)free buffer header,那么它檢查它是否被頻繁訪問。如果被頻繁訪問,服務(wù)器進(jìn)程將promote這個(gè)buffer header,然后繼續(xù)掃描。如果這個(gè)free buffer header不被頻繁訪問,服務(wù)器進(jìn)程將使用從磁盤讀取到的數(shù)據(jù)塊來替換這個(gè)buffer,并更新buffer header,移動(dòng)buffer header到LRU chain的midpoint。注意這里不需要更新CBC結(jié)構(gòu),因?yàn)閎uffer沒有被移動(dòng),只有LRU chain上的buffer header被移動(dòng)。如果服務(wù)器進(jìn)程遇到一個(gè)dirty buffer header,那么檢查是否是一個(gè)被頻繁訪問的dirty buffer header。如果dirty buffer header被頻繁訪問,它將promote這個(gè)buffer header并繼續(xù)掃描。如果dirty buffer header不被頻繁訪問,服務(wù)器進(jìn)程將移動(dòng)這個(gè)buffer header到寫列表中。如果服務(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ù)庫寫進(jìn)程在詢問“每個(gè)buffer的touch count數(shù)是多少?"時(shí),它實(shí)際是問“buffer的touch count是否大于或等于_db_aging_hot_criteria?”。如果每隔幾秒一個(gè)buffer就會(huì)被touch,那么它應(yīng)該被保留在cache中。如果不是,它將被快速替換掉。
當(dāng)一個(gè)被頻繁訪問的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ù)庫寫進(jìn)程也可能promote被頻繁訪問的buffer headers。當(dāng)一個(gè)數(shù)據(jù)庫寫進(jìn)程處于休眠狀態(tài),它將每3秒鐘被喚醒一次。每個(gè)數(shù)據(jù)庫寫進(jìn)程都有一個(gè)屬于它的寫列表(dirty列表)并且它也與一個(gè)或多個(gè)LRU chain相關(guān)聯(lián)。當(dāng)一個(gè)LRU chain的數(shù)據(jù)庫寫進(jìn)程被喚醒,它將檢查它的寫列表來查看寫列表的長度是否足夠執(zhí)行一個(gè)IO寫操作。如果數(shù)據(jù)庫寫進(jìn)程決定構(gòu)建一個(gè)寫列表,它將掃描它的LRU chain來查找不被頻繁訪問的dirty buffer。非常像服務(wù)器進(jìn)程查找free buffer那樣,數(shù)據(jù)庫寫進(jìn)程也將獲得相關(guān)的LRU chain lath,從LRU chain的LRU端開始并檢查buffer header是否為dirty且不被頻繁訪問。如果一個(gè)不被頻繁訪問的dirty buffer被找到,數(shù)據(jù)庫寫進(jìn)程將會(huì)這個(gè)buffer header從LRU chain移動(dòng)到它的寫列表中(記住,這個(gè)buffer header仍然存放在CBC結(jié)構(gòu)中,因此它能被其它進(jìn)程找到)。如果寫列表的長度仍然不足夠執(zhí)行一次IO寫操作,那么數(shù)據(jù)庫寫進(jìn)程將繼續(xù)掃描它的LRU chain,查找更多的不被頻繁訪問的dirty buffer headers。
Hot Region to Cold Region Movement
一個(gè)buffer header的生命周期在LRU chain是從midpoint(正中間)開始的。因?yàn)槠渌黚uffer headers將被替換并且被插入到正中間,隨著buffers被promoted,一個(gè)buffer header自然地將遷移到LRU chain的LRU端。promote一個(gè)buffer header的唯一方法就是buffer header標(biāo)識(shí)為被頻繁訪問。當(dāng)一個(gè)buffer跨過正中間(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一次來使其匹配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ì)疑問為什么當(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è)置可能最初看起來比較愚蠢,但它確實(shí)有其真正的目的并為將來奠定了基礎(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ì)飆升到無窮大。touch count重新設(shè)置也意味著最被頻繁訪問的buffer headers將不需要有最高的touch counts。如果你注意到一個(gè)特定的buffer有一個(gè)較低的touch count,那么你可能捕獲了一個(gè)被頻繁訪問的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)行組合來使用微不足道的競爭來啟用高性能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之前的版本中,等待事件被簡化成latch free,為了判斷特定的latch,需要使用v$session_wait視圖中的p2列與v$latch中的latch#進(jìn)行關(guān)聯(lián)來進(jìn)行查詢。對(duì)于Oracle 10g及以后的版本,等待事件標(biāo)識(shí)為latch:cache buffers lru chain。
如果不需要執(zhí)行物理讀來從磁盤讀取數(shù)據(jù),那么就不會(huì)存在LRU chain latch競爭,因?yàn)榫筒恍枰檎襢ree buffer或者插入一個(gè)buffer header到一個(gè)LRU chain中。數(shù)據(jù)庫寫進(jìn)程查找不被頻繁訪問的dirty buffers不會(huì)對(duì)LRU chain結(jié)構(gòu)造成壓力從而導(dǎo)致LRU chain latch的競爭。然而,任何時(shí)候一個(gè)服務(wù)器進(jì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競爭。但如果IO子系統(tǒng)返回?cái)?shù)據(jù)塊的時(shí)間少于5ms,那么壓力就轉(zhuǎn)移到CPU子系統(tǒng)了,并且這時(shí)LRU chain的活動(dòng)將開始承受壓力。
LRU chain latch競爭可能的結(jié)果是獲取latch的問題,持有l(wèi)atch大長時(shí)間或者兩個(gè)同時(shí)出現(xiàn)。如果操作系統(tǒng)的CPU受限,獲得latch可能花費(fèi)很長時(shí)間,因?yàn)闆]有足夠的CPU周期。一旦latch被獲得且LRU chain相關(guān)的內(nèi)核代碼被運(yùn)行,如果CPU周期供應(yīng)不足或者不被頻繁訪問的free buffers有限,LRU chain latch可能被持有很長時(shí)間足夠造成嚴(yán)重的競爭。
因此,首先,必須要有強(qiáng)烈的物理讀取活動(dòng)。第二,IO子系統(tǒng)響應(yīng)時(shí)間非??欤瑢⒋蟛糠值牡却龝r(shí)間從讀取等待事件傳遞到LRU chain latch等待事件。這種競爭提供了許多可供組合使用解決方法:
.優(yōu)化物理IO SQL語句
如果沒有物理IO存在就不會(huì)有大量的LRU chain latch競爭。因此,從應(yīng)用程序角度來說產(chǎn),查找主要活動(dòng)為執(zhí)行物理塊讀取也就是物理IO活動(dòng)的SQL語句。盡你所能地減少SQL語句的物理IO消耗。這意味著執(zhí)行經(jīng)典的SQL優(yōu)化操作,包括使用索引,以及在性能關(guān)鍵時(shí)期減少頂級(jí)物理IO SQL語句的執(zhí)行速度。
.增加CPU處理能力
與CBC latch競爭一樣或任何其它latch競爭一樣,如果有更多的CPU資源可以使用,內(nèi)存管理將會(huì)花費(fèi)更少的時(shí)間。這意味著latch持有時(shí)間與latch獲取時(shí)間(spinning與sleeping)將被減少。增加CPU處理能力也意味著在競爭高峰期間尋找創(chuàng)建性方法來減秒CPU消耗。
.增加LRU latch數(shù)量
通過增加latches可以增加LRU的并發(fā),這意味著增加隱含參數(shù)_db_block_lru_latches的值。如果有很多G的buffer cache增加latches可能是特別有效的。
.使用多個(gè)buffer pools
一種創(chuàng)造性策略來減少主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ù)庫寫進(jìn)程之前更多的時(shí)間來被touched。
_db_aging_touch_time參數(shù),它的缺省值為3它是唯一能增加一個(gè)buffer header的touch count(x$bh.tch)時(shí)間窗口的方法。增加這個(gè)參數(shù)將減小突然爆發(fā)以buffer為中心活動(dòng)的影響,同時(shí)會(huì)冒著貶值頻繁被訪問buffer的風(fēng)險(xiǎn)。
_db_aging_hot_criteria參數(shù),它的缺省值為2。一個(gè)buffer header的touch count閾值必須滿足或被超過才能被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。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。