buffer cache實驗1-記憶體結構圖解

還不算暈發表於2014-02-17

1.為什麼要使用buffer cache???

buffer cache就是一塊含有許多資料塊的記憶體區域,這些資料塊主要都是資料檔案裡的資料塊內容的拷貝。
從buffer cache中讀取一個資料塊一般需要100ns左右,從一般的儲存硬碟中讀取一個資料塊需要10ms;所以大概算一下,從記憶體中讀取資料塊比從硬碟中快近十萬倍。
故oracle在讀取資料塊時,先在buffer cache中查詢,如存在,則讀取--邏輯讀;如果資料塊不存在,則發生物理讀,從物理檔案中將資料讀入buffer cache(不考慮直接讀的情況)。
在初始化引數中,設定buffer cache大小的引數是db_cache_size
在11.2.0.4.0中此引數支援動態修改:
BYS@ bys3>show parameter db_cache_size
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_cache_size                        big integer 48M
BYS@ bys3>alter system set db_cache_size=40M;
System altered.
BYS@ bys3>show parameter db_cache_size
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_cache_size                        big integer 40M

buffer cache所提供的功能主要包括:

1) 通過快取資料塊,從而減少I/O。
2) 通過構造CR塊,從而提供讀一致性功能。
3) 通過提供各種lock、latch機制,從而提供多個程式併發訪問同一個資料塊的功能。

2.buffer cache的記憶體結構圖解

話說大神們都用WORD畫圖,為了模仿大神, 我也用WORD畫了好久的原創:

從這張buffer cache的記憶體結構圖,用一句話來說明
buffer cache中 結構大致是:buffer pool--->working set--->CBC latch--->hash bucket--->hash chain--->buffer header--->buffer dba
下面就把這些結構的概念大致說明一下: --更詳細的的在之後


1.buffer header:

每一個資料塊在被讀入buffer cache時,都會先在buffer cache中構造一個buffer header,buffer header與資料塊一一對應(buffer header 中有指定buffer 具體記憶體地址的資訊)。

buffer header包含的主要資訊有:   詳見:
1) 該資料塊在buffer cache中實際的記憶體地址。
2) 該資料塊的型別,包括data、segment header、undo header、undo block等等。
3) 該buffer header所在的hash chain,是通過在buffer header裡儲存指向前一個buffer header的指標和指向後一個buffer header的指標的方式實現的。
4) 該buffer header所在的LRU、LRUW、CKPTQ等連結串列(這些連結串列我們後面都會詳細說明)。也是通過記錄前後buffer header指標的方式實現。
5) 當前該buffer header所對應的資料塊的狀態以及標記。
6) 該buffer header被訪問(touch)的次數。
7) 正在等待該buffer header的程式列表(waiter list)和正在使用該buffer header的程式列表(user list)。

我的測試環境:buffer cache大小是40M,buffer的個數是4936(每個buffer在x$bh中都存在一條記錄)。 在11G中db_cache_size 是一個動態引數,可以手動更改此引數後再查詢x$bh,可以發現buffer的個數也會隨之變化的。
SYS@ bys3>show parameter db_cache_si     如果是動態SGA管理,應該查:select * from v$sga_dynamic_components;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_cache_size                        big integer 40M
SYS@ bys3>select count(*) from x$bh;   --用這句查出buffer的個數,也就是可以存放4936個資料塊(因為還有一部分空間是生成buffer header),db_cache_size/4936-8K就能算出一個buffer header的大致大小
  COUNT(*)
----------

      4936

BH buffer header----block_buffers塊個數是一一對應的,事實上相等
BH的大小計算-- 即db_cache_size的大小減去block_buffers*8K   --這裡資料庫的預設塊大小是8K

10G中BH的大小:
9I據說是 188byte.
SYS@ ocm1>select 48*1024*1024/5988-8192 from dual;
48*1024*1024/5988-8192
----------------------
            213.418838

在資料庫中知道 FILE# BLOCK#如何查詢ba buffer address

SYS@ bys3>select ba from x$bh where dbarfil=4 and dbablk=171;   --32位LINUX,需要用SYS使用者查X$BH
BA
--------
207A4000    ---記憶體中的位置,16進位制,一個16進製表示4bit,對應32位OS

################################

2.hash chain與hash bucket:

從上圖中可以看到,一個hash bucket是對應著一條hash chain的。Hash Bucket-直譯叫hash桶
Hash Bucket   一個邏輯上的概念,通過對buffer header 裡記錄的資料塊地址和資料塊型別運用hash演算法以後,得到的組號。
hash chain    將屬於同一個hash bucket的所有buffer header串起來的連結串列

伺服器程式將資料塊讀取到buffer cache後,將資料塊的DBA進行HASH運算,將具有相同HASH值的資料塊的buffer header掛載到同一個hash bucket下(可能多個塊的HASH值相同),並用hash chain串聯起來。
buffer cache中,預設的hash bucket的數量或者說預設有多少條hash chain連結串列,是由一個隱藏引數: _db_block_hash_buckets決定的。
關於_db_block_hash_buckets引數的取值:據說在8i下,該引數預設為db_block_buffers×2;但是到了9i以後,該引數似乎取的是小於且最接近於db_block_buffers×2的素數。
在ORACLE 10G和11G中,預設值是大於2倍的buffer數量的最小的2的冪的值。舉例如buffer數量是500,2倍就是1000,那麼大於1000的最小的2的冪的值是1024,也就是就會有1024個hash bucket。

在我測試系統中:buffer 數量是4936,2倍是9872,從隱含引數_db_block_hash_buckets  查出bufket數量是16384 ,完全符合。
SYS@ bys3>select count(*) from x$bh;   --用這句查出buffer的個數
  COUNT(*)
----------
      4936
P_NAME                                   P_DESCRIPTION                                      P_VALUE                        ISDEFAULT ISMODIFIED ISADJ
---------------------------------------- -------------------------------------------------- ------------------------------ --------- ---------- -----
_db_block_hash_buckets                   Number of database block hash buckets              16384                          TRUE      FALSE  FALSE
_db_block_hash_latches                   Number of database block hash latches              1024                           TRUE      FALSE  FALSE
SYS@ bys3>show parameter db_block_size
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_block_size                        integer     8192


而一條hash chain上的buffer header數量,沒有固定限制(CR塊有限制,一條hash chain上的CR塊不能超過6個)。從隱含引數_db_block_max_cr_dba中可以查到這個限制:
P_NAME                                   P_DESCRIPTION                                      P_VALUE                        ISDEFAULT ISMODIFIED ISADJ
---------------------------------------- -------------------------------------------------- ------------------------------ --------- ---------- -----
_db_block_max_cr_dba                     Maximum Allowed Number of CR buffers per dba       6                              TRUE      FALSE  FALSE

判斷是否有過長的hash chain的語句:過長的hash chain更容易引起熱鏈進而引起CBC LATCH 
SYS@ bys3>select * from (select hladdr,count(*) from x$bh group by hladdr order by 2 desc) where rownum<5;   --x$bh.hladdr表示的是hash chain latch address
HLADDR     COUNT(*)
-------- ----------
2A3A46CC         14
2A7F0864         14
2A3A4EAC         13
2A7F26EC         12
在熱鏈問題發生時,可以通過兩種方法來增加hash chain數量:1、調整隱含引數_db_block_hash_buckets --有風險   2.按ORACLE 10G和11G中,bucket數量的預設值是大於2倍的buffer數量的最小的2的冪的值的公式,來計算出讓系統自動調整bucket數量時buffer cache需要增加到的大小。--查出現在的_db_block_hash_buckets 數量,除以2,將得出值乘以當前資料塊大小(暫不考慮bh大小,也可以把一個bh大小按1K或512bytes來計算),就可以得出要調整到的buffer cache大小。--注意注意:這個調整重啟後才生效。
按我測試環境中值來計算:_db_block_hash_buckets 16384 ,db_block_size       8192,一個buffer header按512bytes。想讓系統自動調整hash bucket的數量,需要將buffer cache大小調整為大於68M,計算方法如下:
SYS@ bys3>select 16384/2*(8192+512)/1024/1024 "Desired size" from dual;
Desired size
------------
          68
當然了,這種調整buffer cache大小進而增大hash bucket數量的方法是治標了,引起熱鏈問題,不良SQL語句或者高併發是主因,要想從根本上解決熱鏈問題,就要從這些方面入手解決了。--不過要真是buffer cache過小,還是要在系統記憶體資源允許情況下增大點好。
###########################

3.hash latch:就是latch:cache buffers chains --CBC LATCH

用於保護hash chain結構,一個CBC LATCH管理著多個hash chain。
用到此LATCH的場景:
1.服務程式需要掃描hash chain上的buffer header時或者叫要讀取buffer cache中資料塊,
2.伺服器程式要將新讀入的資料塊掛載到hash chain上時,

我的測試系統中:hash_buckets  個數是16384 ,CBC LATCH數量是1024,計算出一個CBC LATCH要管理16個hash_chain

P_NAME                                   P_DESCRIPTION                                      P_VALUE                        ISDEFAULT ISMODIFIED ISADJ
---------------------------------------- -------------------------------------------------- ------------------------------ --------- ---------- -----
_db_block_hash_buckets                   Number of database block hash buckets              16384                          TRUE      FALSE  FALSE
_db_block_hash_latches                   Number of database block hash latches              1024                           TRUE      FALSE  FALSE
SYS@ bys3>select count(*) from v$latch_children where name like '%cache buffers chains%';
  COUNT(*)
----------
      1024
SYS@ bys3>select 16384/1024 from dual;
16384/1024
----------
        16


#############################################################################     
     
4.Data Buffer Cache多緩衝池技術:

指根據資料的不同訪問方式,將Data Buffer Cache分為Default,Keep,Recycle.  Default 池則存放未指定儲存池 的資料,按照LRU演算法管理。Keep 池中 資料傾向於一直儲存,可存放經常使用的資料,Recycle池中資料傾向於即時老化,可以存放一次性讀取使用的資料。預設所有表(未指定儲存的池)使用Default池,大小是資料緩衝區大小。
建立或修改表進指定:alter table test storage(buffer_pool keep);  
BUFFER_POOL { KEEP | RECYCLE | DEFAULT }
語句在10G官方文件-SQL Reference---alter table---storage_clause
ORACLE 9I後一個資料庫可以存在2K/4K/8K/16K/32K這五種大小的block,db_block_size 定義的是主block_size。
如果要在資料庫中建立不同block_size的表空間,就要設定db_nk_cache_size引數。
每個pool會有至少8個“Latch:cache buffers lru chain”.

5.working set:

每個working set都具有它自己的一組LRU和LRUW連結串列(LRU和LRUW連結串列總是成對出現的)。
ORACLE為了提高buffer cache效能(大記憶體),使用了多個working set
每個working set都由一個名為“Latch:cache buffers lru chain”的latch來保護,每一個lru latch對應一個working set。
而每個被載入到buffer cache的buffer header都以輪詢的方式掛到working set上去。
而每個被載入到buffer cache的buffer header都以輪詢的方式掛到working set上去。也就是說,當buffer cache載入一個新的資料塊時,其對應的buffer header會去找一個可用的lru latch,如果沒有找到,則再找下一個lru latch,直到找到為止。如果輪詢完所有的lru latch也沒能找到可用的lru latch,該程式只有等待latch free等待事件,同時出現在v$session_wait中,並增加“latch misses”。
如果啟用了多個DBWR後臺程式的話,每個DBWR程式都會對應一個不同的working set,而且每個DBWR只會處理分配給它的working set,不會處理其他的working set。

虛擬機器中,CPU是一個,在10G中是有8個LRU LATCH,在11GR2中,是16個LRU LATCH.
SYS@ bys3>select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0      Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

SYS@ bys3>@?/rdbms/admin/show_para
Enter value for p: lru_latch
old   3: WHERE i.inst_id = USERENV ('Instance') AND CV.inst_id = USERENV ('Instance') AND i.indx = CV.indx AND upper(i.ksppinm) LIKE upper('%&p%') ORDER BY REPLACE (i.ksppinm, '_', '')
new   3: WHERE i.inst_id = USERENV ('Instance') AND CV.inst_id = USERENV ('Instance') AND i.indx = CV.indx AND upper(i.ksppinm) LIKE upper('%lru_latch%') ORDER BY REPLACE (i.ksppinm, '_', '')

P_NAME                                   P_DESCRIPTION                                      P_VALUE                        ISDEFAULT ISMODIFIED ISADJ
---------------------------------------- -------------------------------------------------- ------------------------------ --------- ---------- -----
_db_block_lru_latches                    number of lru latches                              8                              TRUE      FALSE    FALSE

BYS@ bys3>show parameter cpu_c
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cpu_count                            integer     1
BYS@ bys3>select addr,child#,name from v$latch_children where name ='cache buffers lru chain';

ADDR         CHILD# NAME
-------- ---------- ----------------------------------------------------------------
290A80DC         16 cache buffers lru chain
290A8058         15 cache buffers lru chain
297FC670         14 cache buffers lru chain
297FC5EC         13 cache buffers lru chain
297883B8         12 cache buffers lru chain
29788334         11 cache buffers lru chain
29714100         10 cache buffers lru chain
2971407C          9 cache buffers lru chain
2969FE48          8 cache buffers lru chain
2969FDC4          7 cache buffers lru chain
2962BB90          6 cache buffers lru chain
2962BB0C          5 cache buffers lru chain
295B78D8          4 cache buffers lru chain
295B7854          3 cache buffers lru chain
29543620          2 cache buffers lru chain
2954359C          1 cache buffers lru chain
SYS@ bys3>select id,name,block_size,current_size,target_size from v$buffer_pool;

        ID NAME                 BLOCK_SIZE CURRENT_SIZE TARGET_SIZE
---------- -------------------- ---------- ------------ -----------
         3 DEFAULT                    8192           36          36
###################3
SYS@ ocm1>select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SYS@ ocm1>@?/rdbms/admin/show_para
Enter value for p: lru_latch
old   3: WHERE i.inst_id = USERENV ('Instance') AND CV.inst_id = USERENV ('Instance') AND i.indx = CV.indx AND upper(i.ksppinm) LIKE upper('%&p%') ORDER BY REPLACE (i.ksppinm, '_', '')
new   3: WHERE i.inst_id = USERENV ('Instance') AND CV.inst_id = USERENV ('Instance') AND i.indx = CV.indx AND upper(i.ksppinm) LIKE upper('%lru_latch%') ORDER BY REPLACE (i.ksppinm, '_', '')
P_NAME                                   P_DESCRIPTION                                      P_VALUE                        ISDEFAULT ISMODIFIED ISADJ
---------------------------------------- -------------------------------------------------- ------------------------------ --------- ---------- -----
_db_block_lru_latches                    number of lru latches                              8                              TRUE      FALSE    FALSE

SYS@ ocm1>show parameter cpu_c
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cpu_count                            integer     1
SYS@ ocm1>
SYS@ ocm1>select name from v$latch_children where name ='cache buffers lru chain';
NAME
--------------------------------------------------
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
8 rows selected.

SYS@ ocm1>select id,name,block_size,current_size,target_size from v$buffer_pool;
        ID NAME                 BLOCK_SIZE CURRENT_SIZE TARGET_SIZE
---------- -------------------- ---------- ------------ -----------
         3 DEFAULT                    8192           48          48

每個working set中,還有其它的佇列:ckpt queue,ObjectQ、FileQ等,根據塊的不同,也可能會同時掛載在這些佇列下。
working set與lru latch一一對應。lru latch的數量是由一個隱藏引數:_db_block_lru_latches決定的。
該引數預設值為DBWR程式的數量×8。該引數最小必須為8,如果強行設定比8小的數值,oracle將忽略你設定的值,而使用8作為該引數值。
###############################################

6.最後結合上圖:用一段話來簡潔的概括buffer cache的記憶體結構:


buffer cache中有pool,每個buffer pool使用自己的cache buffers lru chain LATCH,一個資料庫例項可以配置:DEFAULT,2KB,4KB,8KB,16KB,32KB,KEEP,RECYCLE 這8種型別的buffer pool,故cache buffers lru chain LATCH的數量最少為8個。每個Latch:cache buffers lru chain對應著一個working set,
每個working set中,有多個CBC LATCH來,每個CBC LATCH管理著多個hash bucket,每個hash bucket對應著一條hash chain。
資料塊在讀入buffer cache中時,同時會在buffer cache中構造一個buffer header,ORACLE對資料塊的DBA進行hash運算,根據運算結果將buffer header掛載到相應的hash chain上。
同時,每個working set中,有一對LRU和LRUW連結串列,buffer cache中的資料塊,勝塊會掛載到LRUW連結串列,其它塊在LRU連結串列。也就是buffer cache中的資料塊,肯定在LRU和LRUW連結串列之一:不能同時存在這兩個連結串列上。




相關文章