暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SYSTEM表空间- Oracle中独一无二的Cache对象

原创 eygle 2019-10-14
965

SYSTEM表空间是Oracle数据库最重要的表空间,在创建数据库时被最先创建,其中包含了数据库的元数据,对于数据库来说生死攸关。


系统表空间是永远不能OFFLINE的,如果SYSTEM表空间OFFLINE,则数据库就无法打开,如果SYSTEM表空间出现故障,则数据库就需要进行介质恢复。在数据库的启动过程中,Oracle也需要通过SYSTEM表空间进行引导。


SYSTEM表空间及root dba


在系统表空间文件头存在一个重要的数据结构root dba,我们可以通过转储数据文件头获得这个信息。在5.2.2中我们曾经提到过,转储文件头可以通过如下命令:

alter session set events 'immediate trace name FILE_HDRS level 10'

从生成的trace文件中,可以获得如下信息(Oracle 9i环境信息摘录):

FILE HEADER:
        Software vsn=153092096=0x9200000, Compatibility Vsn=134217728=0x8000000
        Db ID=1407686520=0x53e79778, Db Name='EYGLE'
        Activation ID=0=0x0
        Control Seq=1299570=0x13d472, File size=27017=0x6989
        File Number=1, Blksiz=8192, File Type=3 DATA
Tablespace #0 - SYSTEM  rel_fn:1
Creation   at   scn: 0x0000.00000007 04/24/2006 11:34:39
Backup taken at scn: 0x0004.6c2d657e 02/12/2007 15:54:52 thread:1
 reset logs count:0x24dc1f7d scn: 0x0004.6c432ec0 recovered at 04/07/2007 21:04:11
 status:0x4 root dba:0x004001a1 chkpt cnt: 6939 ctl cnt:6938


root dba仅在SYSTEM表空间的文件头存在,用于定位数据库引导的bootstrap$信息。root dba存储的是用十六进制表示的二进制数,其中包含10位的文件号以及22位的数据块号,将0x004001a1转换为二进制就是0000 0000 0100 0000 0000 0001 1010 0001,前10位为1,代表文件号为1,后22位转换为十进制为417,代表数据文件1上的417号数据块。


当然在数据库中无须如此复杂,我们可以用5.3.2中提到的getbfno来转换上述root dba:

SQL> select getbfno('0x004001a1') bfno from dual;
BFNO
------------------------------
datafile# is:1
datablock is:417

 

那么这个Block上存放的是什么对象呢?


Oracle中独一无二的Cache对象


可以查询一下数据库中file 1 block 417上存放的对象:

SQL> select segment_name,segment_type,header_file,header_block
  2  from dba_segments where segment_type='CACHE';
 
SEGMENT_NA SEGMENT_TYPE       HEADER_FILE HEADER_BLOCK
---------- ------------------ ----------- ------------
1.417      CACHE                        1          417

在Oracle 9i中这里存放的是Oracle数据库中独一无二的Cache对象。这个对象的名称来自于文件号和数据块号,1.417正好就是文件1的第417个数据块。


这个Cache对象在Oracle数据库中的含义非同一般,在数据库启动的bootstrap过程中,这个对象之前的所有对象都需要用来bootstrap:

SQL> select b.object_id,a.segment_name,a.segment_type,a.header_block from dba_segments a,dba_objects b
  2  where a.segment_name=b.object_name(+) and a.header_file=1 and a.header_block <= 417
  3  order by a.header_block
  4  /
 
 OBJECT_ID SEGMENT_NAME                   SEGMENT_TYPE       HEADER_BLOCK
---------- ------------------------------ ------------------ ------------
           SYSTEM                         ROLLBACK                      9
         2 C_OBJ#                         CLUSTER                      25
         3 I_OBJ#                         INDEX                        49
         6 C_TS#                          CLUSTER                      57
         7 I_TS#                          INDEX                        65
         8 C_FILE#_BLOCK#                 CLUSTER                      73
         9 I_FILE#_BLOCK#                 INDEX                        81
        10 C_USER#                        CLUSTER                      89
        11 I_USER#                        INDEX                        97
        15 UNDO$                          TABLE                       105
        17 FILE$                          TABLE                       113
        18 OBJ$                           TABLE                       121
        23 PROXY_DATA$                    TABLE                       129
        24 I_PROXY_DATA$                  INDEX                       137
        25 PROXY_ROLE_DATA$               TABLE                       145
        26 I_PROXY_ROLE_DATA$_1           INDEX                       153
        27 I_PROXY_ROLE_DATA$_2           INDEX                       161
        28 CON$                           TABLE                       169
        29 C_COBJ#                        CLUSTER                     177
        30 I_COBJ#                        INDEX                       185
        33 I_TAB1                         INDEX                       193
        34 I_UNDO1                        INDEX                       201
        35 I_UNDO2                        INDEX                       209
        36 I_OBJ1                         INDEX                       217
        37 I_OBJ2                         INDEX                       225
        38 I_OBJ3                         INDEX                       233
        39 I_IND1                         INDEX                       241
        40 I_ICOL1                        INDEX                       249
        41 I_FILE1                        INDEX                       257
        42 I_FILE2                        INDEX                       265
        43 I_TS1                          INDEX                       273
        44 I_USER1                        INDEX                       281
        45 I_COL1                         INDEX                       289
        46 I_COL2                         INDEX                       297
        47 I_COL3                         INDEX                       305
        48 I_CON1                         INDEX                       313
        49 I_CON2                         INDEX                       321
        50 I_CDEF1                        INDEX                       329
        51 I_CDEF2                        INDEX                       337
        52 I_CDEF3                        INDEX                       345
        53 I_CDEF4                        INDEX                       353
        54 I_CCOL1                        INDEX                       361
        55 I_CCOL2                        INDEX                       369
        56 BOOTSTRAP$                     TABLE                       377
           1.417                          CACHE                       417
 
45 rows selected.


这些对象在CREATE DATABASE过程中通过sql.bsq文件创建,其对象号同样代表了这些对象的创建顺序。再来看看1.417对象中存储的信息,转储数据块可以使用如下命令:

alter system dump datafile 1 block 417


检查生成的跟踪文件,可以获得主要信息如下:

Start dump data blocks tsn: 0 file#: 1 minblk 417 maxblk 417
buffer tsn: 0 rdba: 0x004001a1 (1/417)
scn: 0x0004.6c4f907a seq: 0x01 flg: 0x04 tail: 0x907a0d01
frmt: 0x02 chkval: 0xc5e1 type: 0x0d=Compatibility segment
Header:  size 12   next rdba 0x0   entries 25   offset 536f
Compatibility entry for 'COMPATSG':
   Size: 24  Release 0x134217728  By 0x153092096
Dump of memory from 0x0AA84E34 to 0x0AA84E38
AA84E30          00000000                        [....]       
Compatibility entry for 'BOOTSTRP':
   Size: 24  Release 0x134217728  By 0x153092096
Dump of memory from 0x0AA84E4C to 0x0AA84E50
AA84E40                            00400179              [y.@.]


这个信息记录了BOOTSTRP的信息,00400179正好指向的是file 1 block 377:

SQL> select getbfno('0x00400179') bfno from dual;
BFNO
------------------------------
datafile# is:1
datablock is:377


这一数据块上存储的正是数据库的BOOTSTRP$引导表:

SQL> select segment_name from dba_extents
  2  where block_id between 377 and blocks + 377 -1;
SEGMENT_NAME
------------------------------------------------------------------
BOOTSTRAP$


而1.417对象的前一个对象正是BOOTSTRAP$,这个对象中存放的就是创建这些对象的语句,这些语句在数据库启动时,需要被获取以在内存中创建这些对象,再从硬盘上加载启动数据库必须的元数据,从而启动Oracle数据库:

SQL> select * from bootstrap$ order by obj#;
     LINE#       OBJ# SQL_TEXT
---------- ---------- --------------------------------------------------
        -1         -1 8.0.0.0.0
         0          0 CREATE ROLLBACK SEGMENT SYSTEM STORAGE (  INITIAL
                      112K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 32765 OBJN
                      O 0 EXTENTS (FILE 1 BLOCK 9))
 
         2          2 CREATE CLUSTER C_OBJ#("OBJ#" NUMBER) PCTFREE 5 PCT
                      USED 40 INITRANS 2 MAXTRANS 255 STORAGE (  INITIAL
                       136K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 214748364
                      5 PCTINCREASE 0 OBJNO 2 EXTENTS (FILE 1 BLOCK 25))
                       SIZE 800
 
         3          3 CREATE INDEX I_OBJ# ON CLUSTER C_OBJ# PCTFREE 10 I
                      NITRANS 2 MAXTRANS 255 STORAGE (  INITIAL 64K NEXT
                       1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCRE
                      ASE 0 OBJNO 3 EXTENTS (FILE 1 BLOCK 49))
……………………

 

注意在Oracle 10g/11g中,Cache对象已经不再存在:

SQL> select * from v$version where rownum <2;
BANNER
-----------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
 
SQL> select count(*) from dba_segments where segment_type='CACHE';
  COUNT(*)
----------
         0
 
SQL> select * from v$version where rownum <2;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
 
SQL> select count(*) from dba_segments where segment_type='CACHE';
  COUNT(*)
----------
         0


此时Oracle的root dba里直接存储了指向obj#=56的object,也就是存储指向了表bootstrap$的数据块的地址。


     我们现在来模拟一下在10g/11g里,Oracle在启动数据库的时候是如何找到表bootstrap$和bootstrap$里存储的各个基表的创建语句的。


首先,Oracle会去读1号数据文件(通常是system表空间的第一个datafile,如system01.dbf )的文件头,Oracle通过读取文件头里记录的root dba,就知道了指向表bootstrap$的数据块的地址:

BBED> set file 1 block 1
        FILE#           1
        BLOCK#          1
 
BBED> p kcvfh.kcvfhrdb
ub4 kcvfhrdb                                @96       0x00400179


从上述结果里我们可以看到,现在root dba指向了0x00400179(即file 1,block 377),接着Oracle会去读root dba指向的数据块,以获得bootstrap$的对象号和其存储的各个基表的创建语句:

BBED> set dba 0x00400179
        DBA             0x00400179 (4194681 1,377)
 
BBED> p ktemh
struct ktemh, 16 bytes                      @92     
   ub4 count_ktemh                          @92       0x00000001
   ub4 next_ktemh                           @96       0x00000000
   ub4 obj_ktemh                            @100      0x00000038
   ub4 flag_ktemh                           @104      0x40000000


可以看到这里root dba指向的object的对象号实际上是0x00000038,也就是10进制的56,即bootstrap$。

SQL> select to_number('38','XX') from dual;
TO_NUMBER('38','XX')
--------------------
                  56
SQL> select name from sys.obj$ where obj#=56;
NAME
------------------------------
BOOTSTRAP$


Oracle已经通过root dba找到了表bootstrap$的对象号(obj#),但现在的问题是:只知道bootstrap$的obj#是不够的,bootstrap$中的记录到底存在哪里?


答案是表bootstrap$中记录的存储地址实际上是存在root dba所指向的数据块的ktetb这个结构里。


好了,我们这里来证明上述观点:

SQL> desc bootstrap$;
Name     Type           Nullable Default Comments
-------- -------------- -------- ------- --------
LINE#    NUMBER                                   
OBJ#     NUMBER                                  
SQL_TEXT VARCHAR2(4000)                          
 
SQL> select count(*) from bootstrap$;
  COUNT(*)
----------
        57
 
SQL> select * from bootstrap$ where obj#=0;
   LINE#   OBJ#   SQL_TEXT
--------------- --------- ----------------------------------------------------------------------------------------------         0          0 CREATE ROLLBACK SEGMENT SYSTEM STORAGE (  INITIAL 112K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 32765 OBJNO 0 EXTENTS (FILE 1 BLOCK 9))
 
SQL> select dump(0,16) from dual;
 
DUMP(0,16)
---------------
Typ=2 Len=1: 80


上述记录里SQL_TEXT为“CREATE ROLLBACK SEGMENT SYSTEM STORAGE (  INITIAL 112K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 32765 OBJNO 0 EXTENTS (FILE 1 BLOCK 9))”,在ultraEdit里看一下其16进制编码,上述语句的16进制编码为:

43 52 45 41 54 45 20 52 4F 4C 4C 42 41 43 4B 20
53 45 47 4D 45 4E 54 20 53 59 53 54 45 4D 20 53
54 4F 52 41 47 45 20 28 20 20 49 4E 49 54 49 41
4C 20 31 31 32 4B 20 4E 45 58 54 20 31 30 32 34
4B 20 4D 49 4E 45 58 54 45 4E 54 53 20 31 20 4D
41 58 45 58 54 45 4E 54 53 20 33 32 37 36 35 20
4F 42 4A 4E 4F 20 30 20 45 58 54 45 4E 54 53 20
28 46 49 4C 45 20 31 20 42 4C 4F 43 4B 20 39 29
29

                                   

按照上述观点,在ktetb[0]. ktetbdba所指向的的那个block里一定会有一条记录,这条记录有三个column,这三个column的值分别为80,80和

43 52 45 41 54 45 20 52 4F 4C 4C 42 41 43 4B 20
53 45 47 4D 45 4E 54 20 53 59 53 54 45 4D 20 53
54 4F 52 41 47 45 20 28 20 20 49 4E 49 54 49 41
4C 20 31 31 32 4B 20 4E 45 58 54 20 31 30 32 34
4B 20 4D 49 4E 45 58 54 45 4E 54 53 20 31 20 4D
41 58 45 58 54 45 4E 54 53 20 33 32 37 36 35 20
4F 42 4A 4E 4F 20 30 20 45 58 54 45 4E 54 53 20
28 46 49 4C 45 20 31 20 42 4C 4F 43 4B 20 39 29
29

 

好了,我们现在来验证一下:

BBED> set dba 0x00400179
        DBA             0x00400179 (4194681 1,377)
 
BBED> p ktetb
struct ktetb[0], 8 bytes                    @108    
   ub4 ktetbdba                             @108      0x0040017a
ub4 ktetbnbk                             @112      0x00000007
 
BBED> set dba 0x0040017a
        DBA             0x0040017a (4194682 1,378)
 
BBED> set offset 8030
        OFFSET          8030
 
BBED> dump
 File: /iprat02/ipratest/system01.dbf (1)
 Block: 378              Offsets: 8030 to 8191           Dba:0x0040017a
------------------------------------------------------------------------
 2c010301 80018081 43524541 54452052 4f4c4c42 41434b20 5345474d 454e5420
 53595354 454d2053 544f5241 47452028 2020494e 49544941 4c203131 324b204e
 45585420 31303234 4b204d49 4e455854 454e5453 2031204d 41584558 54454e54
 53203332 37363520 4f424a4e 4f203020 45585445 4e545320 2846494c 45203120
 424c4f43 4b203929 292c0103 033e6466 033e6466 09382e30 2e302e30 2e30015b
 0601


这里我们可以看到,bootstrap$ 中obj#=0的那条记录确实是存在上述block中,即我们的观点得到证明。


另外,我们从上述证明过程中也可以看出Oracle在10g/11g中会首先访问root dba(这里是0x00400179,即file 1 block 377)以获得表bootstrap$的对象号(obj#)和结构ktetb中所指向的存储bootstrap$中记录的数据块地址,接着就会去读取这个数据块(这里是0x0040017a,即file 1 block 378)以获得bootstrap$存储的各个基表的创建语句。关于这一点,我们也可以从下一节中我们会提到的Oracle在open过程中的10046事件的trace文件中得到佐证:


WAIT #1: nam='db file sequential read' ela= 35 file#=1 block#=377 blocks=1 obj#=-1 tim=17525910818553
WAIT #1: nam='db file scattered read' ela= 7618 file#=1 block#=378 blocks=3 obj#=-1 tim=17525910826329


现在我们来总结一下——Oracle在启动数据库的过程中,会先去读1号数据文件的文件头中记录的root dba,再通过root dba去找bootstrap$中存储的那些数据字典的基表的定义,最后根据这些定义创建数据字典。即所谓的一生二,二生三,三生万物。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论