打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
新书连载:Oracle数据库的初始化与跟踪学习方法

编辑说明:《Oracle性能优化与诊断案例精选》出版以来,收到很多读者的来信和评论,我们会通过连载的形式将书中内容公布出来,希望书中内容能够帮助到更多的读者朋友们。

相关阅读推荐:

新书连载:Oracle数据库的跟踪和分析方法

10046 事件是 Oracle 提供的内部跟踪事件,是对 SQL_TRACE 的增强,通过10046可以通知Oracle内核执行SQL_TRACE类的跟踪操作。如果我们需要获得更多的跟踪信息,就需要用到10046事件,而在实际工作中最常用的就是10046事件。

自11g开始,10046获得了更多的增强,包括明确的在设置中指定SQL_trace,类似'SQL_trace wait=false, bind=true'这样的设定。

在DBA的职业生涯中,会面临众多的挑战,其中最重要的一种情况是数据库无法启动,所以深入理解Oracle数据库的初始化非常重要。通过Oracle的跟踪手段,可以帮助我们获取这些知识,在我的学习过程中,一直在不断地通过跟踪去研究熟悉的或不熟悉的特性和功能,从而加深自己对于数据库的理解。

对于Oracle数据库的初始化,我最初的思考是:数据库的核心信息都是存放在数据文件当中的,但是当数据库尚未打开之前,Oracle是无法获得这部分数据的。那么Oracle是怎样完成这个从数据文件到内存的初始化过程的呢?

首先通过以下步骤对数据库的OPEN过程进行跟踪,研究获得的跟踪文件。

SQL> startup mount;

SQL> alter session set events='10046 trace name context forever,level 12';

SQL> alter database open;

以上通过10046跟踪获得一个跟踪文件,跟踪文件里将记录从mount到open的过程中,Oracle所执行的后台操作。可以通过tkprof工具对跟踪文件进行格式化,使得其中的信息更便于阅读。

首先我们来参考跟踪文件的前面部分(我的研究首先从Oracle 9i开始,逐渐推演到Oracle 12c,研究不同版本的引导过程方法完全相同),这是第一个对象的创建。

create table bootstrap$ 

( line# number not null, 

obj# number not null, 

SQL_text varchar2(4000) not null) 

storage (initial 50K objno 56 extents (file 1 block 520))

注意:在这一步骤中,实际上Oracle是在内存中创建bootstrap$的结构,然后从数据文件中读取数据到内存中,完成第一次初始化。在9i中,读取的位置是文件1的377块,自从11g之后变更为文件1的520块。注意此处的file 1 block 520子句是内部语句,意味这这些对象的存储位置是固定的,该语法对用户创建对象是不可用的。

从数据库的创建脚本 $ORACLE_HOME/rdbms/admin/SQL.bsq 文件中,可以获得bootstrap$表的初始创建语句,直至12c这些定义未曾变化(在12c中SQL.bsq分解为一系列的bsq文件,dcore.bsq中记录了下面这段代码)。

create table bootstrap$

( line#         number not null,                          /* statement order id */

  obj#          number not null,                           /* object number */

  SQL_text      varchar2("M_VCSZ") not null)            /* statement */

  storage (initial 50K)           /* to avoid space management during IOR I */

//                                            /* "//" required for bootstrap */

接下来从数据库中查询一下,file 1 block 520 上存储的是什么对象。

SQL> select segment_name,file_id,block_id

  2  from dba_extents where block_id=520 and file_id=1;

SEGMENT_NAME       FILE_ID   BLOCK_ID

--------------- ----------  ---------

BOOTSTRAP$               1        520

File 1 Block 520开始存放的正是Bootstrap$对象。继续查看Trace文件的内容,Oracle进一步执行的是如下操作。

select line#, SQL_text from bootstrap$ where obj# != :1

在创建并从数据文件中装载了bootstrap$的内容之后,Oracle开始递归的从该表中读取信息,加载数据。那么bootstrap$中记录的是什么信息呢?

在数据库中,bootstrap$是一张实际存在的系统表。

SQL> desc bootstrap$

 Name             Null?    Type

 --------------- -------- --------------------

 LINE#            NOT NULL NUMBER

 OBJ#             NOT NULL NUMBER

 SQL_TEXT         NOT NULL VARCHAR2(4000)

来看一下这张表的具体内容。

以上输出只显示了表中的4条记录,大家可以自行研究一下其他记录的内容。从这些语句中可以看出,bootstrap$ 中实际上是记录了一些数据库系统基本对象的创建语句。Oracle通过bootstrap$进行引导,进一步创建相关的重要对象,从而启动了数据库。

如果向前追溯,可以继续考察一下bootstrap$的创建过程。查看一下创建数据库的脚本,可以发现数据库在创建过程中最先运行的是一个叫做CreateDB.SQL的脚本。这个脚本发出CREATE DATABASE的命令,具体类似如下的例子。

CREATE DATABASE eygle

MAXINSTANCES 1 MAXLOGHISTORY 1 MAXLOGFILES 5 MAXLOGMEMBERS 3 MAXDATAFILES 100

DATAFILE '/opt/oracle/oradata/eygle/system01.dbf' 

SIZE 250M REUSE AUTOEXTEND ON NEXT  10240K MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL

DEFAULT TEMPORARY TABLESPACE TEMP TEMPFILE '/opt/oracle/oradata/eygle/temp01.dbf' 

SIZE 40M REUSE AUTOEXTEND ON NEXT  640K MAXSIZE UNLIMITED

UNDO TABLESPACE "UNDOTBS1" DATAFILE '/opt/oracle/oradata/eygle/undotbs01.dbf' 

SIZE 200M REUSE AUTOEXTEND ON NEXT  5120K MAXSIZE UNLIMITED

CHARACTER SET ZHS16GBK NATIONAL CHARACTER SET AL16UTF16

LOGFILE GROUP 1 ('/opt/oracle/oradata/eygle/redo01.log') SIZE 10240K,

GROUP 2 ('/opt/oracle/oradata/eygle/redo02.log') SIZE 10240K,

GROUP 3 ('/opt/oracle/oradata/eygle/redo03.log') SIZE 10240K;

exit; 

在这个创建过程中,Oracle会隐含的调用$ORACLE_HOME/rdbms/admin/SQL.bsq脚本,用于创建数据字典。这个文件的位置受到一个隐含的初始化参数 (_init_SQL_file )的控制。

SQL> @GetParDescrb.SQL 

Enter value for par: init_SQL

NAME            VALUE                 DESCRIB

--------------- --------------------- --------------------------------------------

_init_SQL_file  

?/rdbms/admin/SQL.bsq 

File containing SQL statements to execute upon database creation

如果在创建过程中,Oracle无法找到SQL.bsq文件,则数据库创建将会出错。我们可以测试一下移除SQL.bsq文件,再看这样一个数据库创建过程。

SQL> startup nomount;

SQL> @CreateDB.SQL

CREATE DATABASE eygle

*

ERROR at line 1:

ORA-01092: ORACLE instance terminated. Disconnection forced

此时日志中会记录以下信息。

Fri Aug 18 15:45:49 2006

Errors in file /opt/oracle/admin/eygle/udump/eygle_ora_3632.trc:

ORA-01501: CREATE DATABASE failed

ORA-01526: error in opening file '?/rdbms/admin/SQL.bsq'

ORA-07391: sftopn: fopen error, unable to open text file.

Error 1526 happened during db open, shutting down database

USER: terminating instance due to error 1526

这就是SQL.bsq文件在数据库创建过程中的作用。那么在数据库的引导过程中,又该如何去定位bootstrap$的位置呢?

这就不得不提到了SYSTEM表空间了。在系统表空间文件头存在一个重要的数据结构root dba,我们可以通过转储数据文件头获得这个信息,从生成的trace文件中,我们可以获得以下信息(Oracle 12c环境信息摘录)。

V10 STYLE FILE HEADER:

    Compatibility Vsn = 202375680=0xc100200

    Db ID=2903506423=0xad0ffdf7, Db Name='PRODCDB'

    Activation ID=0=0x0

    Control Seq=60695=0xed17, File size=103680=0x19500

    File Number=1, Blksiz=8192, File Type=3 DATA

Tablespace #0 - SYSTEM  rel_fn:1 

Creation   at   scn: 0x0000.00000007 07/07/2014 05:38:57

Backup taken at scn: 0x0000.00000000 01/01/1988 00:00:00 thread:0

 reset logs count:0x35f7cd7a scn: 0x0000.0018531f

 prev reset logs count:0x32cc9b67 scn: 0x0000.00000001

 recovered at 03/02/2016 12:59:30

 status:0x2004 root dba:0x00400208 chkpt cnt: 941 ctl cnt:940

root dba仅在SYSTEM表空间的文件头存在,用于定位数据库引导的bootstrap$信息。

Root dba存储的是用16进制表示的二进制数,其中包含10位的文件号以及22位的数据块号,将0x00400208转换为二进制就是0000 0000 0100 0000 0000 0010 0000 1000,前10位为1,代表文件号为1,后22位转换为10进制为520,代表数据文件1上的520号数据块。

当然在数据库中无须如此复杂,Oracle提供工具用于数据块及文件号的转换。

SQL> variable file# number

SQL> execute :file#:=dbms_utility.data_block_address_file(to_number('400208','xxxxxxx'));

PL/SQL procedure successfully completed.

SQL> variable block# number

SQL> execute :block#:=dbms_utility.data_block_address_block(to_number('400208','xxxxxxx'))

PL/SQL procedure successfully completed.

SQL>print file#

     FILE#

----------

         1

SQL> print block#

    BLOCK#

----------

       520

现在可以全面的来回顾一下数据库的内部引导过程,通过10046事件可以跟踪一下数据库的打开过程,使用前面曾经提到过的步骤。

oracle@enmocoredb admin]$ SQLplus / as sysdba

SQL*Plus: Release 12.2.0.0.3 Production on Thu Aug 4 15:26:49 2016

Copyright (c) 1982, 2016, Oracle.  All rights reserved.

SQL> shutdown immediate;

SQL> startup mount;

Database mounted.

SQL> alter session set events='10046 trace name context forever,level 12';

SQL> alter database open;

SQL> shutdown immediate;

从跟踪文件(以下跟踪文件来自Oracle 12.2版本)中我们可以获得以下重要信息。

=====================

PARSING IN CURSOR #0x7fdf6c93ae70 len=19 dep=0 uid=0 oct=35 lid=0 tim=2699657623431 hv=1907384048 ad='0x61ce6470' SQLid='a01hp0psv0rrh'

alter database open

END OF STMT

PARSE #0x7fdf6c93ae70:c=2999,e=2961,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=0,tim=2699657623426

WAIT #0x7fdf6c93ae70: nam='db file sequential read' ela= 12 file#=1 block#=520 blocks=1 obj#=-1 tim=2699658571220

=====================

PARSING IN CURSOR #0x7fdf6c938d48 len=188 dep=1 uid=0 oct=1 lid=0 tim=2699658571988 hv=4006182593 ad='0x61c4bd28' SQLid='32r4f1brckzq1'

create table bootstrap$ ( 

  line#         number not null, 

  obj#           number not null,  

  SQL_text   varchar2(4000) not null) 

 storage (initial 50K objno 59 extents (file 1 block 520))

END OF STMT

PARSE #0x7fdf6c938d48:c=0,e=658,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=0,tim=2699658571988

WAIT #0x7fdf6c938d48: nam='PGA memory operation' ela= 13 p1=65536 p2=2 p3=0 obj#=-1 tim=2699658572091

EXEC #0x7fdf6c938d48:c=0,e=215,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,plh=0,tim=2699658572285

CLOSE #0x7fdf6c938d48:c=0,e=3,dep=1,type=0,tim=2699658572352

=====================

PARSING IN CURSOR #0x7fdf6c938d48 len=65 dep=1 uid=0 oct=3 lid=0 tim=2699658572771 hv=1762642493 ad='0x61c4a500' SQLid='aps3qh1nhzkjx'

select line#, SQL_text from bootstrap$ where obj# not in (:1, :2)

END OF STMT

PARSE #0x7fdf6c938d48:c=0,e=404,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=0,tim=2699658572770

BINDS #0x7fdf6c938d48:

 Bind#0

  oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00

  oacflg=08 fl2=1000001 frm=00 csi=00 siz=24 off=0

  kxsbbbfp=7fdf6c938900  bln=22  avl=02  flg=05

  value=59

 Bind#1

  oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00

  oacflg=08 fl2=1000001 frm=00 csi=00 siz=24 off=0

  kxsbbbfp=7fdf6c9388d0  bln=24  avl=06  flg=05

  value=4294967295

EXEC #0x7fdf6c938d48:c=1000,e=817,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=867914364,tim=2699658573677

WAIT #0x7fdf6c938d48: nam='db file sequential read' ela= 9 file#=1 block#=520 blocks=1 obj#=59 tim=2699658573738

WAIT #0x7fdf6c938d48: nam='PGA memory operation' ela= 9 p1=65536 p2=1 p3=0 obj#=59 tim=2699658573827

WAIT #0x7fdf6c938d48: nam='db file scattered read' ela= 19 file#=1 block#=521 blocks=3 obj#=59 tim=2699658573931

从等待事件上可以明确看到,单块读读取了文件1的第520个数据块,这也正是引导块的定位过程。

WAIT #0x7fdf6c93ae70: nam='db file sequential read' ela= 12 file#=1 block#=520 blocks=1 obj#=-1 tim=2699658571220

了解了SYSTEM表空间的重要作用,也就可以理解,为什么系统表空间的文件头损坏,或者如果启动对象的数据块损坏后,Oracle数据库就将无法启动。

我们曾经见过很多案例,很多用户的数据库运行在非归档模式下,又没有备份,最后当SYSTEM表空间出现故障后,数据库就无法打开了,这是最为严重的情况,通常是没有办法恢复数据的。

所以我们经常反复建议,SYSTEM表空间极其重要,备份重于一切,希望通过我们的不断呼吁,数据库的安全能够更加引起重视,用户的数据能够更加安全。

数据库的引导过程还可以通过GDB工具在Linux、UNIX上进行跟踪,分步骤来观察这个启动过程,以下输出可以帮助读者进一步了解这些内部操作。

首先将数据库启动到Mount状态,找到进程SPID。

SQL> startup mount;

ORACLE instance started.

Database mounted.

SQL> select spid from v$process where addr in (select paddr from v$session where sid=(select distinct sid from v$mystat));

SPID

------------

1518

然后通过gdb跟踪这个进程。

localhost:~ oracle$ gdb $ORACLE_HOME/bin/oracle 1518

GNU gdb 6.3.50-20050815 (Apple version gdb-1518) (Sat Feb 12 02:52:12 UTC 2011)

Attaching to program: `/oracle/product/10.2.0/bin/oracle', process 1518.

Reading symbols for shared libraries .+++++++++++ done

0x00007fff80616984 in read ()

(gdb) 

然后跟踪两个内部指令。

(gdb) break kcrf_commit_force

Breakpoint 1 at 0x1025a2d4c

(gdb) break kqlobjlod

Breakpoint 2 at 0x1006c78b4

此时执行数据库OPEN操作会被挂起。

SQL> alter database open;

然后重新开启一个SQL*Plus进程,查询此时数据库加载的ROWCACHE对象。

SQL> select parameter,count,gets from v$rowcache where count!=0;

no rows selected

然后继续执行,我们看到在第三个步骤之后,数据库加载了一个ROW Cache对象。

(gdb) c

Continuing.

Breakpoint 1, 0x00000001025a2d4c in kcrf_commit_force ()

(gdb) c

Continuing.

Breakpoint 1, 0x00000001025a2d4c in kcrf_commit_force ()

(gdb) c

Continuing.

Breakpoint 2, 0x00000001006c78b4 in kqlobjlod ()

SQL> select parameter,count,gets from v$rowcache where count!=0;

PARAMETER      COUNT  GETS

-------------------------------- ---------- ----------

dc_objects  1     1

这个对象是什么呢?

SQL> select address,cache_name,existent,lock_mode,saddr,substr(key,1,40) keystr from v$rowcache_parent;

ADDRESS CACHE_NAME      E  LOCK_MODE SADDR    KEYSTR

---------------- -------------------- - ---------- ---------------- ----------------------------------------

00000001942E9080 dc_objects       N   3 0000000194782EB0 000000000A00424F4F5453545241502400000000

解析其KEY值,正是bootstrap$,这就是数据库初始化时加载的第一个对象。

SQL> select dump('BOOTSTRAP$',16) from dual;

DUMP('BOOTSTRAP$',16)

--------------------------------------------

Typ=96 Len=10: 42,4f,4f,54,53,54,52,41,50,24

然后数据库将递归查询该对象中的数据,向内存中加载其他对象。更进一步。

这里可以看到数据库加载了回滚段信息,首先加载的是SYSTEM的回滚段,转储Row Cache信息之后,就可以看到这些详细的内容。

SQL>  ALTER SESSION SET EVENTS 'immediate trace name row_cache level 10';

这里得到的BUCKET 37包含了回滚段信息。

其中53595354454d正是SYSTEM回滚段。

SQL> select dump('SYSTEM',16) from dual;

DUMP('SYSTEM',16)

-------------------------------

Typ=96 Len=6: 53,59,53,54,45,4d

而另外一个BUCKET上正是BOOTSTRAP$对象。

这就是数据库启动过程中,BOOTSTRAP$的加载与引导过程。由上面的讨论我们可以知道bootstrap$表的重要,如果bootstrap$表发生损坏,数据库将无法启动。


加入"云和恩墨大讲堂"微信群,参与讨论学习

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
深入了解Oracle数据字典
Oracle 9i 数据库设计指引全集(4)
CentOS5.5下安装Oracle11gR2学习笔记(测试通过)
MySql-Day-01
Oracle11g和Sql 2008R2数据库相互link实际经验
1.oracle登陆
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服