熱點塊的定義
熱點塊的定義
數(shù)據(jù)庫的熱點塊,從簡單了講,就是極短的時間內(nèi)對少量數(shù)據(jù)塊進行了過于頻繁的訪問。定義看起來總是很簡單的,但實際在數(shù)據(jù)庫中,我們要去觀察或者確定熱點塊的問題,卻不是那么簡單了。要深刻地理解數(shù)據(jù)庫是怎么通過一些數(shù)據(jù)特征來表示熱點塊的,我們需要了解一些數(shù)據(jù)庫在這方面處理機制的特性。
數(shù)據(jù)緩沖區(qū)的結構
我們都知道,當查詢開始的時候,進程首先去數(shù)據(jù)緩沖區(qū)中查找是否存在查詢所需要的數(shù)據(jù)塊,如果沒有,就去磁盤上把數(shù)據(jù)塊讀到內(nèi)存中來。在這個過程中,涉及到數(shù)據(jù)緩沖區(qū)中LRU鏈的管理(8i開始以接觸點計數(shù)為標準衡量buffer冷熱從而決定buffer是在LRU的冷端還是熱端),關于這部分內(nèi)容,從oracle concepts 中就能得到詳盡的文檔,我不準備去論述這部分內(nèi)容,這也不是本文的重點。現(xiàn)在我們的重點是,到底進程是如何地去快速定位到自己所想要的block的,或者如何快速確定想要的block不在內(nèi)存中而去進行物理讀的。
我們仔細想一想,隨著硬件的發(fā)展,內(nèi)存越來越大,cache buffer也越來越大,我們?nèi)绾尾拍茉诖罅康膬?nèi)存中迅速定位到自己想要的block?總不能去所有buffer中遍歷吧!在此數(shù)據(jù)庫引出了hash的概念(oracle中快速定位信息總是通過hash算法的,比如快速定位sql是否在shared pool size中存在就是通過hash value來定位的,也就是說shared pool size中對象也是通過hash table來管理的),了解一點數(shù)據(jù)結構的基本知識就知道,hash 的一大重要功能就是快速地查找。舉個最簡單的例子,假設我們有一個hash table 就是一個二維數(shù)組a[200][100],現(xiàn)在有1000個無序數(shù)字,我們要從這1000個數(shù)字里面查找某個值是否存在,或者說當我們接收到某個數(shù)字的時候必須判斷是否已經(jīng)存在,當然,我們可以遍歷這1000個數(shù)字,但這樣的效率就很低。但現(xiàn)在我們考慮這樣一種方法,那就是把1000個數(shù)字除以200,根據(jù)其余數(shù),放在a[200][100]里面(假設相同余數(shù)的最大數(shù)量不超過100),余數(shù)就是數(shù)組的下標。這樣,平均來說一個數(shù)組a[i]里面可能有5個左右的數(shù)字。當我們要去判別一個數(shù)字是否存在的時候,對這個數(shù)字除以200(這就是一個最簡單的hash算法),根據(jù)余數(shù)i作為下標去數(shù)組a[i]中查找,大約進行5次查找就能判別是否已經(jīng)存在,這樣通過開辟內(nèi)存空間a[200][100]來換取了時間(當然hash 算法的選取和hash table的大小是一個很關鍵的問題)。
明白了基本的hash原理之后,我們再來看oracle的block的管理。數(shù)據(jù)庫為這些block也開辟了hash table,假設是a,則在一維上的數(shù)量是由參數(shù)_db_block_hash_buckets 來決定的,也就是存在hash table a[_db_block_hash_buckets ],從oracle8i開始,_db_block_hash_buckets =db_block_buffers*2。而一個block被放到哪個buckets里面,則是由block的文件編號、塊號(x$bh.dbarfl、x$bh.dbablk對應了block的文件屬于表空間中的相關編號和block在文件中的編號,x$bh是所有cache buffer的header信息,通過表格的形式可以查詢)做hash 算法決定放到哪個bucket的,而bucket里面就存放了這些buffers的地址。這樣當我們要訪問數(shù)據(jù)的時候,可以獲得segment的extent(可以通過dba_extents查到看,詳細的信息來源這里不做探討),自然知道要訪問的文件編號和block編號,根據(jù)文件和block編號可以通過hash算法計算出hash bucket,然后就可以去hash bucket里面去找block對應的buffer。
除此之外,為了維護對這些block的訪問和更改,oracle還提供了一種latch來保護這些block。因為要避免不同的進程隨意地徑直并發(fā)修改和訪問這些block,這樣很可能會破壞block的結構的。latch是數(shù)據(jù)庫內(nèi)部提供的一種維護內(nèi)部結構的一種低級鎖,latch的生存周期極短(微秒以下級別),進程加latch后快速的進行某個訪問或者修改動作然后釋放latch(關于latch不再過多的闡述,那可能又是需要另一篇文章才能闡述清楚)。這種latch數(shù)量是通過參數(shù)_db_block_hash_latches 來定義的,一個latch對應的保護了多個buckets。從8i開始,這個參數(shù)的default規(guī)則為:
當cache buffers 少于2052 buffers
_db_block_hash_latches = power(2,trunc(log(2, db_block_buffers - 4) - 1))
當cache buffers多于131075 buffers
_db_block_hash_latches = power(2,trunc(log(2, db_block_buffers - 4) - 6))
當cache buffers位于2052與131075 buffers之間
_db_block_hash_latches = 1024
通過這個規(guī)則我們可以看出,一個latch大約可以維護128個左右的buffers。由于latch使得對block的操作的串行化(9i中有改進,讀與讀可以并行,但讀與寫、寫與寫依然要串行),很顯然我們可以想到一個道理,如果大量進程對相同的block進程進行操作,必然在這些latch上造成競爭,也就是說必然形成latch的等待。這在宏觀上就表現(xiàn)為系統(tǒng)級的等待。明白了這些原理,為我們下面的在數(shù)據(jù)庫中的診斷奠定了基礎。
如何確定熱點對象
如果我們經(jīng)常關注statspack報告,會發(fā)現(xiàn)有時候出現(xiàn)cache buffer chains的等待。這個cache buffer chains就是_db_block_hash_latches所定義的latch的總稱,通過查詢v$latch也可得到:
select">sys@OCN>select latch#,name,gets,misses,sleeps from v$latch where name like 'cache buffer%';
LATCH# NAME GETS MISSES SLEEPS
---------- ------------------------------ ---------- ---------- ----------
93 cache buffers lru chain 54360446 21025 238
98 cache buffers chains 6760354603 1680007 27085
99 cache buffer handles 554532 6 0
在這個查詢結果里我們可以看到記錄了數(shù)據(jù)庫啟動以來的所有cahce buffer chains的latch的狀況,gets表示總共有這么多次請求,misses表示請求失敗的次數(shù)(加鎖不成功),而sleeps 表示請求失敗休眠的次數(shù),通過sleeps我們可以大體知道數(shù)據(jù)庫中l(wèi)atch的競爭是否嚴重,這也間接的表征了熱點塊的問題是否嚴重。由于v$latch是一個聚合信息,我們并不能獲得哪些塊可能存在頻繁訪問。那我們要來看另一個view信息,那就是v$latch_children,v$latch_children.addr記錄的就是這個latch的地址。
select">sys@OCN>select addr,LATCH#,CHILD#,gets,misses,sleeps from v$latch_children
2 where name = 'cache buffers chains' and rownum < 21;
ADDR LATCH# CHILD# GETS MISSES SLEEPS
-------- ---------- ---------- ---------- ---------- ----------
91B23B74 98 1024 10365583 3957 33
91B23374 98 1023 5458174 964 25
91B22B74 98 1022 4855668 868 15
91B22374 98 1021 5767706 923 22
91B21B74 98 1020 5607116 934 31
91B21374 98 1019 9389325 1111 25
91B20B74 98 1018 5060207 994 31
91B20374 98 1017 18204581 1145 18
91B1FB74 98 1016 7157081 920 23
91B
20 rows selected.
到此我們可以根據(jù)v$latch_child.addr關聯(lián)到對應的x$bh.hladdr(這是buffer header中記錄的當前buffer所處的latch地址),通過x$bh可以獲得塊的文件編號和block編號。
select">sys@OCN>select dbarfil,dbablk
from x$bh
where hladdr in
(select addr
from (select addr
from v$latch_children
order by sleeps desc)
where rownum < 11);
DBARFIL DBABLK
---------- ----------
4 6498
40 14915
15 65564
28 34909
40 17987
1 24554
8 21404
39 29669
28 46173
28 48221
……………………
由此我們就打通了cache buffers chains和具體block之間的關系,那再繼續(xù)下來,知道了block,我們需要知道究竟是哪些segment。這個可以通過dba_extents來獲得。
select distinct a.owner,a.segment_name from
dba_extents a,
(select dbarfil,dbablk
from x$bh
where hladdr in
(select addr
from (select addr
from v$latch_children
order by sleeps desc)
where rownum < 11)) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID
OWNER SEGMENT_NAME SEGMENT_TYPE
------------------------------ ------------------------------ ------------------
ALIBABA BIZ_SEARCHER TABLE
ALIBABA CMNTY_USER_MESSAGE TABLE
ALIBABA CMNTY_VISITOR_INFO_PK INDEX
ALIBABA COMPANY_AMID_IND INDEX
ALIBABA COMPANY_DRAFT TABLE
ALIBABA FEEDBACK_POST TABLE
ALIBABA IM_BLACKLIST_PK INDEX
ALIBABA IM_GROUP TABLE
ALIBABA IM_GROUP_LID_IND INDEX
ALIBABA MEMBER TABLE
ALIBABA MEMBER_PK INDEX
ALIBABA MLOG$_SAMPLE TABLE
……………………
我們還有另外一種方式
select object_name
from dba_objects
where data_object_id in
(select obj
from x$bh
where hladdr in
(select addr
from (select addr
from v$latch_children
order by sleeps desc)
where rownum < 11)) ;
OBJECT_NAME
------------------------------------
I_CCOL2
RESOURCE_PLAN$
DUAL
FGA_LOG$
AV_TRANSACTION
COMPANY_DRAFT
MEMBER
SAMPLE
SAMPLE_GROUP
VERTICAL_COMPONENT
MEMBER_PK
SAMPLE_GROUP_PK
IM_BLACKLIST_PK
IM_CONTACT
IM_GROUP
CMNTY_USER_MESSAGE
CMNTY_VISITOR_INFO_PK
IM_OFFLINEMSG_TID_IND
OFFER
OFFER_PK
OFFER_EMAIL_IND
OFFER_DRAFT
CMNTY_USER_MESSAGE_TD_BSM_IND
CMNTY_MESSAGE_NUM_PK
BIZ_EXPRESS_MEMBER_ID_IND
……………………
到這里我們基本能找到熱點塊對對應的對象。但實際上還有另外一個途徑來獲取這些信息,那就是和x$bh.tch 相關的一種方法。對于8i開始oracle提供了接觸點(touch count)來作為block是冷熱的標志,在一定條件滿足的情況下block被進程訪問一次touch count 增加一,到某個標準之后被移動到LRU熱端(關于touch count 在這里不做詳細介紹,那又將是一大篇文章)。那在短時間內(nèi)從某種意義上講,touch count 大的block可能暗示著在當前某個周期內(nèi)被訪問次數(shù)比較多。
select distinct a.owner,a.segment_name,a.segment_type from
dba_extents a,
(select dbarfil,dbablk
from (select dbarfil,dbablk
from x$bh order by tch desc) where rownum < 11) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID
OWNER SEGMENT_NAME SEGMENT_TYPE
------------------------------ ------------------------------ ------------------
ALIBABA CMNTY_USER_MESSAGE TABLE
ALIBABA MEMBER_PK INDEX
ALIBABA OFFER_DRAFT_GMDFY_IND INDEX
同上面一樣還有這個方法
select object_name
from dba_objects
where data_object_id in
(select obj
from (select obj
from x$bh order by tch desc) where rownum < 11) ;
OBJECT_NAME
---------------------------------------------------
DUAL
MEMBER_PK
SAMPLE_GROUP_PK
CMNTY_USER_MESSAGE_TD_BSM_IND
OFFER_DRAFT_MID_GMDFY_IND
OFFER_MID_GPOST_IND
OFFER_DRAFT_PK
MEMBER_GLLOGIN_IND
OFFER_MID_STAT_GEXPIRE_IND
SAMPLE_MID_STAT_IND
10 rows selected.
到這里,我們尋找熱點塊和熱點對象的工作算是完成了,但我們還并沒有解決問題。
熱點問題的解決
熱點塊和熱點對象我們都找到了,但是我們該怎么來解決這個問題呢?一般來說,熱點塊會導致cache buffers chains競爭等待,但并不是說cache buffer chains一定是因為熱點塊而起,在特別情況下有可能是因為latch數(shù)量的問題導致的,也就是一個latch管理的buffers數(shù)量太多而導致競爭激烈。但是latch數(shù)量我們一般是不會輕易去設置的,這是oracle的隱藏參數(shù)。
實際上最有效的辦法,是從優(yōu)化sql入手,不良的sql往往帶來大量的不必要的訪問,這是造成熱點塊的根源。比如本該通過全表掃描的查詢卻走了索引的range scan,這樣將帶來大量的對塊的重復訪問。從而形成熱點問題。再或者比如不當?shù)刈吡薾ested loops的表連接,也可能對非驅動表造成大量的重復訪問。那么在這個時候,我們的目標就是找出這些sql來并嘗試優(yōu)化。在statspack報告中,根據(jù)報告中sql列表,我們?nèi)绻峭ㄟ^dba_extents確定的熱點對象而不是通過dba_objects確定的,則可以通過查找出的熱點segment轉換為對應的表,對于非分區(qū)的索引,index_name就是segment_name,通過dba_indexes很容易的找到對應的table_name,對于分區(qū)表和分區(qū)索引也能通過和dba_tab_partition和dba_ind_partitions找到segment和table的對應關系。通過這些table到statspack報告中去找相關的sql。
select sql_text
from stats$sqltext a,
(select distinct a.owner,a.segment_name,a.segment_type from
dba_extents a,
(select dbarfil,dbablk
from (select dbarfil,dbablk
from x$bh order by tch desc) where rownum < 11) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID
SQL_TEXT
----------------------------------------------------------------
SELECT SEQ_SMS_TRANSACTION.nextval FROM DUAL
SELECT SEQ_BIZ_EXPRESS.nextval FROM DUAL
SELECT bizgroup.seq_grp_post.NextVal FROM DUAL
SELECT SEQ_SAMPLE.nextval FROM DUAL
SELECT bizgroup.seq_grp_user.NextVal FROM DUAL
SELECT SEQ_BIZ_SEARCHER.nextval FROM DUAL
SELECT SEQ_OFFER_DRAFT.nextval FROM DUAL
select seq_Company_Draft.NextVal from DUAL
SELECT SEQ_SAMPLE_GROUP.nextval FROM DUAL
SELECT SEQ_CMNTY_USER_MESSAGE.nextval FROM DUAL
SELECT SYSDATE FROM DUAL
select seq_News_Forum.NextVal from DUAL
SELECT SEQ_SMS_USER.nextval FROM DUAL
select seq_Biz_Member.NextVal from DUAL
select seq_Pymt_Managing.NextVal from DUAL
E= '+08:00' NLS_DUAL_CURRENCY = '$' NLS_TIME_FORMAT = 'HH.MI.SSX
SELECT SEQ_COMPANY_DRAFT.nextval FROM DUAL
SELECT 1 FROM DUAL
select seq_offer_draft.NextVal from DUAL
select seq_Biz_Express_Category.NextVal from DUAL
20 rows selected.
當然這里是從statspack搜集的stats$sqltext中去找的(你可以在statspack的文本報告中去找),實際上,我們可以直接在當前數(shù)據(jù)庫中的v$sqlarea或者v$sqltext里面去找到這些sql,然后來嘗試優(yōu)化。
select sql_text
from v$sqltext a,
(select distinct a.owner,a.segment_name,a.segment_type from
dba_extents a,
(select dbarfil,dbablk
from (select dbarfil,dbablk
from x$bh order by tch desc) where rownum < 11) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID
18 rows selected.
除了優(yōu)化sql外,當然對于熱點的表或者索引來說,如果小的話,我們可以考慮cache在內(nèi)存中,這樣可能降低物理讀提高sql運行速度(這并不會減少cache buffer chains的訪問次數(shù)),對于序列,我們可以對序列多設置一些cache。如果是并行服務器環(huán)境中的索引對象,并且這個索引是系列遞增類型,我們可以考慮反向索引(關于反向索引這里就不過多地做介紹了)。
熱點塊的其他相關癥狀
在數(shù)據(jù)庫中還可能存在一些其他方面的熱點塊癥狀,通過v$waitstat的等待可以看出一些端倪,v$waitstat是根據(jù)數(shù)據(jù)緩沖區(qū)中各種block的類型(x$bh.class)而分類統(tǒng)計的等待狀況。
select">sys@OCN>select * from v$waitstat;
CLASS COUNT TIME
------------------ ---------- ----------
data block 1726977 452542
sort block 0 0
save undo block 0 0
segment header 40 11
save undo header 0 0
free list 0 0
extent map 0 0
1st level bmb 611 112
2nd level bmb 42 13
3rd level bmb 0 0
bitmap block 0 0
bitmap index block 0 0
file header block 13 92
unused 0 0
system undo header 111 28
system undo block 7 0
undo header 2765 187
undo block 633 156
比如在ASSM表空間出現(xiàn)之前,由于freelist的存在,如果表經(jīng)常被并發(fā)的進程DML,則可能存在大量的data block的等待,或者有free list的等待。那么這個時候我們發(fā)現(xiàn)這樣的segment之后需要考慮增加freelist數(shù)量。再比如經(jīng)常發(fā)生長時間的DML的表被頻繁地訪問,這樣將會造成過多的對回滾段中塊的訪問,這時可能undo block 的等待會比較多。那么我們可能需要控制DML的時間長度或者想辦法從應用程序入手來解決問題。如果是undo header的等待比較多,沒使用undo tablespace 之前,可能需要考慮增加回滾段的數(shù)量。
總結
本文從熱點塊的原理入手,詳細地由oracle的內(nèi)部結構特征開始介紹了熱點塊的產(chǎn)生和表現(xiàn)特征。進而闡述了診斷熱點對象和找出造成熱點對象的sql的方法。并從解決熱點問題方面提供了解決方向。
?