ORACLE--SGA之数据缓冲区(Data Buffer)

暨上通过预编译阐述道共享池最后到SGA,这里进一步说明一下SGA中另一个大块,数据缓冲区,携带提及一点数据文件和表空间,后续专门会说明这块。

 

首先了解下SGA种大致有那些东西,这些东西随着数据库版本的增加会有所增加,不过大致上应该一致,这也是基本所有的体系结构都会描述的东西:

 

在认识数据缓冲区前,先记住几个常用的视图:

X$BH、GV$BH、V$BH、V$FIXED_TABLE、V$FIXED_VIEW_DEFINITION、DICT、DICT_COLUMNS。

 

以及两个包:DBMS_SPACE 和 DBMS_ROWID的一些常用内容,在后面会逐渐用到时说明部分使用方式,不过还是先要创建一个表,不然什么测试工作也做不了:

 

在一个用户下创建一个表:

CREATE TABLE TEST_OBJECTS
  AS SELECT * FROM USER_OBJECTS;

 

如果那个用户下没有什么对象,那就多复制几次数据,才能填充一些数据,数据成倍增长:

 

INSERT INTO TEST_OBJECTS
SELECT * FROM TEST_OBJECTS;

COMMIT;

 

 

此时第一个,常规使用下,一个常用的对象就是ROWID,那么看下它由哪几部分组成:

对象编号、文件编号、文件内块号、块内部行号码,我们看下一张表内部每条数据的这些信息:

SQL>SELECT dbms_rowid.rowid_relative_fno(ROWID) "文件编号",
       dbms_rowid.rowid_object(ROWID) "对象编号",
       dbms_rowid.rowid_block_number(ROWID) "块编号",
       dbms_rowid.rowid_row_number(ROWID) "块内部行号码"
    FROM TEST_OBJECTS t;

 

  文件编号   对象编号     块编号 块内部行号码
---------- ---------- ---------- ------------
         4      54646        588            0
         4      54646        588            1
         4      54646        588            2
         4      54646        588            3
         4      54646        588            4
         4      54646        588            5
         4      54646        588            6
         4      54646        588            7
         4      54646        588            8
         4      54646        588            9
         4      54646        588           10
         4      54646        588           11
         4      54646        588           12
         4      54646        588           13
         4      54646        588           14
         4      54646        588           15
         4      54646        589            0
         4      54646        589            1
         4      54646        589            2
         4      54646        589            3

 

其实对象编号是永远不会变化的,一般情况下也在同一个文件中,所以主要看的是BLOCK的信息。

付:文件号是对应数据文件的编号,对应视图DBA_DATA_FILES的FILE_ID字段。

     对象编号是对象在整个数据库的唯一标识,对应DBA_OBJECTS的OBJECT_ID字段。

 

 

此时通过分析表:

BEGIN
     dbms_stats.gather_table_stats('SCOTT','TEST_OBJECTS');
END;

后查询统计信息:

SELECT BLOCKS, NUM_ROWS

FROM USER_TABLES

WHERE TABLE_NAME = 'TEST_OBJECTS';

 

    BLOCKS   NUM_ROWS
---------- ----------
        16       1024

 

再查询用户段信息:

SQL> SELECT t.segment_name,t.blocks FROM USER_SEGMENTS T
  2  WHERE T.segment_name='TEST_OBJECTS';

SEGMENT_NAME                                                                         BLOCKS
-------------------------------------------------------------------------------- ----------
TEST_OBJECTS                                                                             16

 

在查询根据上述统计出来的信息:

SQL> SELECT COUNT(DISTINCT dbms_rowid.rowid_block_number(ROWID))
  2     FROM TEST_OBJECTS;

COUNT(DISTINCTDBMS_ROWID.ROWID
------------------------------
                            13

 

为什么不一样,因为内部有保留块,记录表头、位图信息,数据库始终比这个实际的段要小,通过测试可以发现,每次增长都会以8个块增长,但是数据实际的值,记录实际数据块大小,当发现两者之和大于实际大小,就需要扩展空间了。

 

 

上面有点开场白,这里再想下试验前需要明白,数据缓冲区用于在数据从磁盘中读取数据后到内存是以“”为基本单位,不论块中存储多少行数据,分别有“单块读”和“多块读”两种方式,读入内存后,此时未了避免下次再重磁盘上去读取,减少磁盘读取开销,此时将这些块放入内存中,并采用LRU链表进行管理(ORACLE 8I后并非完全的LRU算法),当对这些数据修改时,内存采用一致性读块进行管理(一个块最多对应6个一致性读块),当数据缓冲区不够用的时候,就会将接使用较少的部分提取出来,将其替换到磁盘上去(其实就是在内存中将其指针删除),为简单证明这个情况,我们需要做一个简单试验,刚才创建的表,我们找几个块出来做实验(588块和589块吧,对象编号上述已经查询出来是:54646,文件号也能通过上述看出是:),我们首先通过X$BH视图查看其实用情况。

 

--X$BH需要在DBA用户下才可以使用,即使在全局数据字典:

V$FIXED_TABLE、V$FIXED_VIEW_DEFINITION、DICT

也找不到他的身影,因为它基本不被外界使用;

顺便说下全局视图:

V$FIXED_TABLE  查询所有的V$视图定义和X$视图信息,GV$视图普遍为集群类使用。

V$FIXED_VIEW_DEFINITION 查询所有V$视图、V$的定义部分

DICT 查询到所有字典的字典列表信息

三者很多信息都是重复的,关键字段有些变化。

通过V$FIXED_VIEW_DEFINITION视图可以找到X$BH定义了GV$BH,由GV$BH的大部分字段组成了V$BH。

另外如果要查询两个块下面的数据如何查询:

定位一下这两个快不是很好查,怎么查询呢,因为只是实验,也不用管太多,只要能得到要的结论即可,此时块信息是需要588~599两个块,那么我们怎么查询数据呢,通过ROWID,就需要DBMS_ROWID包来创建: ROWID >= DBMS_ROWID.ROWID_CREATE(1, 54646, 4, 588, 0)
AND ROWID < DBMS_ROWID.ROWID_CREATE(1, 54646, 4, 600, 0);

DBMS_ROWID.ROWID_CREATE用于创建ROWID,参数按顺序为:类型(直接写1即可),对象编号、文件编号、文件内块号、块内行号。

扯远了,回到话题,开始试验下:

而X$BH视图看下有那些字段(一看看花眼了,找到想要的就行了):

SQL> DESC X$BH
 名称
 -------------
 ADDR
 INDX
 INST_ID
 HLADDR
 BLSIZ
 NXT_HASH
 PRV_HASH
 NXT_REPL
 PRV_REPL
 FLAG
 RFLAG
 SFLAG
 LRU_FLAG
 TS#
 FILE#
 DBARFIL
 DBABLK
 CLASS
 STATE
 MODE_HELD
 CHANGES
 CSTATE
 LE_ADDR
 DIRTY_QUEUE
 SET_DS
 OBJ
 BA
 CR_SCN_BAS
 CR_SCN_WRP
 CR_XID_USN
 CR_XID_SLT
 CR_XID_SQN
 CR_UBA_FIL
 CR_UBA_BLK
 CR_UBA_SEQ
 CR_UBA_REC
 CR_SFL
 CR_CLS_BAS
 CR_CLS_WRP
 LRBA_SEQ
 LRBA_BNO
 HSCN_BAS
 HSCN_WRP
 HSUB_SCN
 US_NXT
 US_PRV
 WA_NXT
 WA_PRV
 TCH
 TIM

需要的字段有:文件号、对象编号、块号、TOUCH热度信息,分别对应字段:file#、obj、dbablk、tch信息,查看一下:

SQL> SELECT file#,dbablk,tch FROM x$bh
  2  WHERE obj = 54646
  3  AND dbablk in(588,589)
  4  ORDER BY dbablk;

     FILE#     DBABLK   TCH
---------- ---------- ----------
         4        588          8
         4        589          8

此时发现已经有一些操作,此时开始对第589块上任意一行数据查询(另起一个会话):

SELECT *
  FROM TEST_OBJECTS T
 WHERE ROWID = DBMS_ROWID.ROWID_CREATE(1, 54646, 4, 589, 0);

然后再执行上述SQL

SQL> /

     FILE#     DBABLK        TCH
---------- ---------- ----------
         4        588          8

         4        589          9 

有兴趣可以多做几次,每次都会有变化,注意,可能对同一个块查询出多条记录,那是一致性读的问题,很多涨块信息也在里面。

然后再执行一下清空数据缓冲区的操作,其实就是清空热点度的问题:

SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;

系统已更改。

SQL> SELECT file#,dbablk,tch FROM x$bh
  2  WHERE obj = 54646
  3  AND dbablk in(588,589)
  4  ORDER BY dbablk;

     FILE#     DBABLK        TCH
---------- ---------- ----------
         4        588          0
         4        589          0
         4        589          0

此时热点度的TCH都是0了,同样的方式可以去查询V$BH视图,V$BH中没有TCH这个字段,对应数据文件字段还是file#,而块字段是block#,提供一个状态字段status和flag字段来源其它视图,status指明被instance共享、专用、还是一致性读块、空闲块;flag按照和1、16、1536等等进行按位求与运算(通过函数bitand)得到块内部的信息,如是否为脏块、是否为临时块等等信息。

数据读取过程有两种方式:单块读(db file sequential)和多块读(db file scattered read),所谓单块读就是每次IO只读取一块到内存,主要看分布情况,一般我们用索引定位少量数据或者用ROWID定位少量数据时较多,但是也有其它情况,和多块读一起说明;多块读是一次读入多个连续块到内存中(一定是连续的),这个数据的上限是由参数db_file_multiblock_read_count参数指定,默认是16个块,可以根据实际情况作相应调整,不过很多时候它是一个理想数字,一般达不到,因为当数据缓冲区不够的时候会替换一些不常用的块到磁盘中,此时数据块在内存中的分布零散的,此时当在查询某个数据范围时,包含多个块,但是这些块在内存中非连续,那么就会按照断掉的部分到磁盘上去读取,此时就会选择单块读还是多块读了,此时较为消耗IO性能,最坏的情况是每个块断一块,所以在业务层面,对于多块信息在考虑查询过程中,保证和查询中的多块读的参数差不多是较为理想的选择(但是这个可能真的不好控制,不过应当有一定的理论计算值),否则在设计层面上,平均性能差距会产生几倍的差距。

另外对于常规小表并且经常查询的或者些经常查询的不太大的索引,此时我们习惯让他们放入内存中,此时使用对表的属性修改为内存表格即可:

ALTER TABLE <table_name> CACHE;

另外可以指定存放位置,默认情况下,ORACLE现在只会给与一个db_cache_size,它是常规的数据缓冲区信息,其实数据缓冲区还有两块分别为:db_keep_cache_size为几乎不会被替换出内存的区域,和db_recycle_cache_size几乎使用完就会被替换出内存的区域(付:如果为ORACLE 9i以前,不包括ORACLE 9i使用的参数为:db_block_buffer、db_pool_keep、db_pool_recycle)。

查询这几个参信息:

show parameter db_cache_size;

show parameter db_keep_cache_size;

show parameter db_recycle_cache_size;

或者通过视图:

SELECT * FROM v$buffer_pool_statistics;

若发现后两者没有数据则修改这记者的大小信息:

SQL>alter system set db_cache_size = 400M;
SQL>alter system set db_keep_cache_size = 150M;
SQL>alter system set db_recycle_cache_size = 50M;

这里需要注意的是:若要增加后面两者,需要先减少前者的大小,因为数据缓冲区毕竟还是一个整体,另外你设置的大小未必是实际的大小,ORACLE会根据你的设置做相应的细节调整,调整完成后,可以SHOW PARAMETER查看大小变化。

如上述表需要设置到KEEP区域进行存储,此时使用:

ALTER TABLE <table_name> STORAGE(BUFFER_POOL KEEP) cache;

同理其它区域分别设置:DEFAULT、RECYCLE,如果将一个已经是cache表修改为非cache表,此时将参数修改为nocache即可。

修改为cache表后,可以通过视图查看:

SELECT name,owner,type
FROM v$db_object_cache where kept = 'YES'
AND NAME ='表名称大写';

或者查询表的属性信息查看:

SELECT a.cache,a.BUFFER_POOL,a.table_name

 FROM user_tables a
 WHERE a.table_name = ‘表名称大写’;

从数据缓冲区可以看出内部运作和读取方式,根据实际情况中的一些便要设计中有一定用处,这也是一些基础,在后面文件管理(其实文件系统才是真正的数据库,现在说的都是实例内部的信息,他们是完全不同的概念),和索引原理上结合企业也许会更加清楚吧。

关于下一次应该是涉及物理结构的文件系统或者实例另一部分PGA相关说明。。。。

上一篇:ORACLE--预编译与共享池--SGA基本介绍


下一篇:DataWorks数据建模 - 一揽子数据模型管理解决方案 | 《一站式大数据开发治理DataWorks使用宝典》