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$表发生损坏,数据库将无法启动。


原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2017-03-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据之美

Hive 中内部表与外部表的区别与创建方法

先来说下Hive中内部表与外部表的区别: Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径, 不对数据的位...

2129
来自专栏C/C++基础

ERROR 1062 (23000) at line 1: Duplicate entry '1332883220' for key 'group_key'

我有一个数据表,记录一个QQ号加好友的活跃天数、加好友次数、加好友的toUin数等信息。数据表的建表语句如下:

764
来自专栏乐沙弥的世界

Oracle 分区表

随着表的不断增大,对于新纪录的增加、查找、删除等(DML)的维护也更加困难。对于数据库中的超大型表,可通过把它的数据分成若干个小表,从而简化数据库的管理活动。...

602
来自专栏大数据和云计算技术

MongoDB Compass--MongoDB DBA必备的管理工具

MongoDB Compass是MongoDB官网提供的一个集创建数据库、管理集合和文档、运行临时查询、评估和优化查询、性能图表、构建地理查询等功能为一体的...

3625
来自专栏企鹅号快讯

Access+Mssql+Mysql 手工注入基本流程

0x01 Access 注入 ? 判断表名:adminand (select count(*) from admin)>0判断字段名:username,pass...

1925
来自专栏运维技术迷

连仕彤博客Mysql数据库归档利器之pt-archiver

1566
来自专栏乐沙弥的世界

只读表空间的备份与恢复

--====================== --  只读表空间的备份与恢复 --====================== 一、只读表空间的特性...

662
来自专栏青枫的专栏

day28_Struts2综合案例

a、拷贝必要的jar包(图中黄色框框) 和 与数据库操作有关的jar包与配置文件(图中绿色框框)

381
来自专栏乐沙弥的世界

SQL*Loader使用方法

SQL*Loader由一个输入控制文件来控制整个装载的相关描述信息,一个或多个数据文件作为原始数据,其详细组成结构包括

582
来自专栏抠抠空间

MySQL 之 视图、触发器、存储过程、函数、事物与数据库锁

浏览目录: 1.视图 2.触发器 3.存储过程 4.函数 5.事物 6.数据库锁 7.数据库备份 1.视图 视图:是一个虚拟表,其内容由查询定义。同真实的表...

3307

扫码关注云+社区