一、概念引出
在数据文件(datafile)中,存放的是表,而数据文件本身是被分成大小相等的若干块,而每个块的大小一般为8k,也可以为2k,4k,16k,32k。
范例:查看当前数据库块的大小
SQL>show parameter block
NAME TYPE VALUE
-------------------------------- ----------- ------------
db_block_buffers integer 0
db_block_checking string FALSE
db_block_checksum string TRUE
db_block_size integer 8192
db_file_multiblock_read_count integer 16
观察可知,当前块大小为8k。还有一点需要知道:ORACLE中I/O的最小单位就是ORACLE块。而与数据文件中“块”对应的就是buffercache中的“buffer”,即在磁盘上我们称为“块(block)”,而在内存中我们称为“buffer”。因为buffer的大小与块的大小相同,所以可以通过查找当前buffercache的大小和当前块的大小去推算出当前DB中共有几多个块。
范例:计算当前buffercache的总buffer数
SQL>show parameter db_cache_size
NAME TYPE VALUE
------------------- ------------- ------------------
db_cache_size biginteger 200M -- buffer cache的大小
计算公式:db_cache_size/db_block_size = 200M / 8192k = 25600 个
所以,当前buffer cache中共有25600个buffer。
一般原则,数据文件中一个块存储多个行的,极少情况下多个块存储一行。Server process在读取块的,是把整个块读进buffer中,所以每个“块(block)”与每个“buffer”的大小是相等的。
据说还有一个可以计算整个数据库所有文件中block的总和:??但是结果与上面不同,是否是因为这个是计算整个数据库的,而上面的只是计算当前buffercache中存了多少个buffer?
SQL>select sum(blocks) from dba_data_files;
SUM(BLOCKS)
-----------
117760
二、buffer cache作用
(1)缓存datafile,从而减少I/O。
(2)构造CR([C]onsistent [R]ead)块。顾名思义,一致读即为了ORACLE在进行DML操作时,不阻塞SELECT操作。当select读取的行在另外会话被修改过而未被提交时,则会在buffer cache中根据UNDO的相关信息创建一个与被修改前一样的块供select读取,所以CR块同样在buffer中。
三、buffer cache的构造
1、CBC(cache bufferchain)
CBC链负责把共享同一个HASH值的BH(buffer header,用于存储buffer的地址)串起来。
我们通过SQL语句执行过程再来分析一下,客户端发出SQL语句,serverprocess拿到语句后,通过计算,分析该语句应该挂在buffer cache的什么位置,然后去CBC中遍历BH,如果找不到,再去硬盘(datafile)中把相应的块读到buffer里面,接下来就把这个buffer的地址信息(BH)再挂到CBC链。以上过程都由server process完成。
2、LRU、LRUW
将脏块写回磁盘的后台进程是DBWn,DBWn的主要任务是负责buffer cache中的脏块合理,及时地写回到磁盘。触发DBWn的发生有几点:
(1)每隔3秒(由“增量检查点”决定);
(2)当server process在buffer中寻找可用buffer(free或clean)时,但检索到了一定的程度还没有找到,此时也会触发DBWn把部分的脏块写回磁盘;
(3)数据库关闭时,DBWn会把脏块全部写回磁盘。
范例:设置DBWn的数量
SQL>show parameter writer
NAME TYPE VALUE
--------------------------- -------- -----------------
db_writer_processes integer 1
SQL>alter system set db_writer_processes = 2 scope=spfile;
Systemaltered.
SQL>startup force -- 使用此语句重启DB十分危险,不建议在生产环境使用
ORACLEinstance started.
TotalSystem Global Area 578813952 bytes
FixedSize 2022472 bytes
VariableSize 348128184 bytes
DatabaseBuffers 226492416 bytes
RedoBuffers 2170880 bytes
Database mounted.
Databaseopened.
SQL>show parameter writer
NAME TYPE VALUE
--------------------------- -------- -----------------
db_writer_processes integer 2
SQL> !ps-ef|grep oracle -- 查看当前ORACLE进程
……
oracle 16799 1 0 10:22 ? 00:00:00 ora_mman_wolex
oracle 16801 1 0 10:22 ? 00:00:00 ora_dbw0_wolex
oracle 16803 1 0 10:22 ? 00:00:00 ora_dbw1_wolex
oracle 16805 1 0 10:22 ? 00:00:00 ora_lgwr_wolex
oracle 16807 1 0 10:22 ? 00:00:00 ora_ckpt_wolex
……
3、checkpointqueue(检查点队列)
四、buffer cache参数配置
在OLTP系统中,对于DB_CACHE_SIZE的设置,推荐配置是:
DB_CACHE_SIZE = SGA_MAX_SIZE/2~SGA_MAX_SIZE*2/3
范例:查看buffercache的大小,并尝试调整。
SQL>select component,current_size,min_size from v$sga_dynamic_components;
COMPONENT CURRENT_SIZE MIN_SIZE
------------------------- ------------ ----------
shared pool 289406976 285212672
large pool 4194304 4194304
java pool 4194304 4194304
streams pool 0 0
DEFAULT buffer cache 12582912 12582912
KEEP buffer cache 0 0
RECYCLE buffer cache 0 0
DEFAULT 2K buffer cache 0 0
DEFAULT 4K buffer cache 0 0
DEFAULT 8K buffer cache 0 0
DEFAULT 16K buffer cache 0 0
DEFAULT 32K buffer cache 0 0
ASM Buffer Cache 0 0
13 rows selected.
SQL> alter system set db_cache_size = 200M scope =both;
alter system set db_cache_size = 200M scope = both
*
ERROR at line 1:
ORA-32017: failure in updating SPFILE
ORA-00384: Insufficient memory to grow cache
以上的buffer cache大小是由SGA_TARGET默认设置的,一般来说buffer cache的值应该满足:DB_CACHE_SIZE =SGA_MAX_SIZE/2~ SGA_MAX_SIZE*2/3。因此可以看出shared_pool在这里设置的大小并不合理。
还有一个问题就是,我们现在并不能把buffer cache的大小调大,因为SGA_TARGET已经把内存分配给了各个池,没有足够内存去扩大buffer cache了。调大方法及步骤如下:
调大SGA_MAX_SIZE → 重启DB → 调大SGA_TARGET → 调大buffer cache
SQL>show parameter sga
NAME TYPE VALUE
------------------------------------ ----------- -----------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 300M
sga_target big integer 300M
SQL>alter system set sga_max_size = 500M scope = spfile;
System altered.
SQL>startup force
ORACLE instance started.
Total System Global Area 524288000 bytes
Fixed Size 2022080 bytes
Variable Size 503317824 bytes
Database Buffers 16777216 bytes
Redo Buffers 2170880 bytes
Database mounted.
Database opened.
SQL>alter system set sga_target = 500M scope = both;
System altered.
SQL>show parameter sga
NAME TYPE VALUE
------------------------------------ ----------- -----------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 500M
sga_target big integer 500M
SQL>alter system set db_cache_size = 200M scope = both ;
System altered.
SQL>select component,current_size,min_size from v$sga_dynamic_components;
COMPONENT CURRENT_SIZE MIN_SIZE
------------------------- ------------ ----------
shared pool 285212672 285212672
large pool 4194304 4194304
java pool 4194304 4194304
streams pool 0 0
DEFAULT buffer cache 226492416 12582912
KEEP buffer cache 0 0
RECYCLE buffer cache 0 0
DEFAULT 2K buffer cache 0 0
DEFAULT 4K buffer cache 0 0
DEFAULT 8K buffer cache 0 0
DEFAULT 16K buffer cache 0 0
DEFAULT 32K buffer cache 0 0
ASM Buffer Cache 0 0
13 rows selected.
范例:通过分析判断buffer cache该设置多大
SQL> setlinesize 100
SQL> setpagesize 40
SQL> select size_for_estimate"Cache Size (MB)",
size_factor,
buffers_for_estimate"Buffers",
estd_physical_read_factorest_read_factor,
estd_physical_readsest_phy_red,
estd_physical_read_timeest_phy_red_t
from v$db_cache_advice
where name = 'DEFAULT'
and block_size=( select value
from v$parameter
where
name='db_block_size');
Cache Size(MB) SIZE_FACTOR BuffersEST_READ_FACTOR EST_PHY_RED EST_PHY_RED_T
-------------------------- ---------- --------------- ----------- -------------
20 .0926 2475 1.0898 2538 0
40 .1852 4950 1 2329 0
60 .2778 7425 1 2329 0
80 .3704 9900 1 2329 0
100 .463 12375 1 2329 0
120 .5556 14850 1 2329 0
140 .6481 17325 1 2329 0
160 .7407 19800 1 2329 0
180 .8333 22275 1 2329 0
200 .9259 24750 1 2329 0
216 1 26730 1 2329 0
220 1.0185 27225 1 2329 0
240 1.1111 29700 1 2329 0
260 1.2037 32175 1 2329 0
280 1.2963 34650 1 2329 0
300 1.3889 37125 1 2329 0
320 1.4815 39600 1 2329 0
340 1.5741 42075 1 2329 0
360 1.6667 44550 1 2329 0
380 1.7593 47025 1 2329 0
400 1.8519 49500 1 2329 0
21 rowsselected.
buffer状态
● x$bh视图
从x$bh视图中可以发现有一列state,表示的就是buffer状态,每个数字的含义如下:
0:free,no valid block image
1:xcur,a current mode block,exclusive to thisinstance
2:scur,a current mode block,shared with other instance
3:cr,a consistent read (stale) block image
4:read,buffer is reserved for a block being read from disk
5:mrec,a block in media recovery mode
6:irec,a block in instance (crash) recovery mode
xcur块指的是从磁盘中读入到buffer cache中的块;当会话A对一个xcur块进行修改后而没有提交,此xcur块即成为了脏块,而此时如果会话B需要对此xcur块进行读操作,但由于会话A未进行提交,所以会话B无法访问此xcur块,所以ORACLE会根据UNDO中的记录值在buffer cache中创建一个cr块,此cr块对应与会话B要访问的xcur块,但此cr块记录的是它对应的xcur块被修改前的值。
下面做一系列实验,查看块当前的各种状态
SQL> startup force
ORACLEinstance started.
Total SystemGlobal Area 524288000 bytes
FixedSize 2022080 bytes
VariableSize 293602624 bytes
DatabaseBuffers 226492416 bytes
RedoBuffers 2170880 bytes
Databasemounted.
Databaseopened.
SQL> select o.object_name,
2 decode(state,0,'free',1,'xcur',2,'scur',3,'cr',4,'read',5,'mrec',
3 6,'irec',7,'write',8,'pi')state,
4 count(*) blocks
5 from x$bh b,dba_objects o
6 where b.obj = o.data_object_id
7 and o.object_name = 'T1'
8 group by o.object_name,state
9 order by blocks desc;
no rowsselected -- DB开始一启动的时候,buffer cache并没有T1的记录
SQL> select * from t1;
ID NAME
------------------------------
1 wolex
2 alex
3 john
SQL> select o.object_name,
decode(state,0,'free',1,'xcur',2,'scur',3,'cr',4,'read',5,'mrec',
6,'irec',7,'write',8,'pi') state,
count(*) blocks
from x$bh b,dba_objects o
where b.obj = o.data_object_id
and o.object_name = 'T1'
group by o.object_name,state
order by blocks desc;
OBJECT_NAME STATE BLOCKS
---------------------------------------------------- ----------
T1 xcur 2
T1 cr 1
通过观察可以发现DB启动后,第一次对T1表进行查询操作时,也会有cr块产生,个人估计原因可能是第一次查询buffer cache里面还没有缓存?但之后的同一会话中查询操作并不会产生cr块,继续看实验:
SQL> alter system flushbuffer_cache;
-- 此语句会清空buffercache,实际生产环境中应谨慎使用
Systemaltered.
SQL> select o.object_name,
2 decode(state,0,'free',1,'xcur',2,'scur',3,'cr',4,'read',5,'mrec',
3 6,'irec',7,'write',8,'pi')state,
4 count(*) blocks
5 from x$bh b,dba_objects o
6 where b.obj = o.data_object_id
7 and o.object_name = 'T1'
8 group by o.object_name,state
9 order by blocks desc;
OBJECT_NAME STATE BLOCKS
---------------------------------------------------- ----------
T1 free 2
SQL> select * from t1;
ID NAME
------------------------------
1 wolex
2 alex
3 john
SQL> select o.object_name,
2 decode(state,0,'free',1,'xcur',2,'scur',3,'cr',4,'read',5,'mrec',
3 6,'irec',7,'write',8,'pi')state,
4 count(*) blocks
5 from x$bh b,dba_objects o
6 where b.obj = o.data_object_id
7 and o.object_name = 'T1'
8 group by o.object_name,state
9 order by blocks desc;
OBJECT_NAME STATE BLOCKS
---------------------------------------------------- ----------
T1 xcur 2
T1 free 2
我们发现即使是清空了buffer cache,但还是有free块,为什么呢?应该是buffer cache中的buffer被清空了,但是CBC并没有被清空,链上挂的地址内容依然指向到buffer里面(即使状态为free)。
接着上面的实验往下做,向T1表中插入一条数据,但不commit,然后再开一个会话进行查询操作:
SQL> insert into t1values(4,'smith');
1 row created.
SQL> select o.object_name,
2 decode(state,0,'free',1,'xcur',2,'scur',3,'cr',4,'read',5,'mrec',
3 6,'irec',7,'write',8,'pi')state,
4 count(*) blocks
5 from x$bh b,dba_objects o
6 where b.obj = o.data_object_id
7 and o.object_name = 'T1'
8 group by o.object_name,state
9 order by blocks desc;
OBJECT_NAME STATE BLOCKS
---------------------------------------------------- ----------
T1 xcur 2
T1 free 2
可以发现在当前会话中并没有cr块产生,所以再另外开一个会话,进行查询操作:
Session_02 > select * from t1;
ID NAME
------------------------------
1 wolex
2 alex
3 john
4 smith
回到刚才的会话,查看块状态:
SQL> select o.object_name,
2 decode(state,0,'free',1,'xcur',2,'scur',3,'cr',4,'read',5,'mrec',
3 6,'irec',7,'write',8,'pi')state,
4 count(*) blocks
5 from x$bh b,dba_objects o
6 where b.obj = o.data_object_id
7 and o.object_name = 'T1'
8 group by o.object_name,state
9 order by blocks desc;
OBJECT_NAME STATE BLOCKS
---------------------------------------------------- ----------
T1 xcur 2
T1 cr 1
范例:查看一个表的rowid和此表全部列
SQL> select rowid,t1.* from t1;
ROWID ID NAME
---------------------------- --------------------
AAAM2IAABAAAO6SAAA 1 wolex