1

Oracle DBA核心技能:数据库的跟踪和分析方法之 10046与等待事件

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

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

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

在常规的使用方法下,10046事件的以下几个跟踪级别最为常用(后面的参数是11g的新设置中可用的参数设定)。
1 - 启用标准的SQL_TRACE功能,等价于SQL_trace
4 - Level 1 加上绑定值(bind values) [ bind=true ]
8 - Level 1 + 等待事件跟踪 [ wait=true ]
12 - Level 1 + Level 4 + Level 8

从11g开始增加了以下两个跟踪级别。
16 – 为每次SQL执行生成STAT信息输出 [ plan_stat=all_executions ]
32 – 不转储执行统计信息 [ plan_stat=never ]

从 11.2.0.2开始增加以下级别。
64 – 自适应的STAT转储 [ plan_stat=adaptive ]

我们知道,在进行数据库问题诊断及性能优化时,经常需要查询的几个重要视图包括vsession_wait、vsystem_event等,这些视图中主要记录的就是等待事件。

通过调整以降低等待,是提高性能的一个方法。这些等待事件来自所有数据库操作,对于不同进程的等待可以通过动态性能视图vsession_wait等来查询;对于数据库全局等待可以通过vsystem_event等视图来获得。

同样的,通过10046事件对具体session进行跟踪,可以获得每个session的执行情况及等待事件、统计信息等详细信息,输出到外部跟踪文件以进行分析。

类似SQL_trace,10046事件可以在全局设置,也可以在session级设置。在全局设置,可以在参数文件中增加Event参数,此设置对所有用户的所有进程生效,包括后台进程,所以同样除了研究目的应当很少被使用。

event=“10046 trace name context forever,level 12”

通过alter session的方式修改,需要alter session的系统权限。

SQL> alter session set events '10046 trace name context forever, level 12'; -- 启用跟踪
SQL> alter session set events '10046 trace name context off'; --停止跟踪

11g之后的可选设置方式类似。
alter session set events ‘SQL_trace wait=true’;

对其他用户session设置事件跟踪,同样可以通过DBMS_SYSTEM的SET_EV过程来实现。

PROCEDURE SET_EV
 Argument Name	           Type          In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SI                   BINARY_INTEGER          IN
 SE                   BINARY_INTEGER          IN
 EV                   BINARY_INTEGER          IN
 LE                   BINARY_INTEGER          IN
 NM                   VARCHAR2         IN

其中的参数SI、SE来自v$session视图。
查询获得需要跟踪的session信息。

SQL> select sid,serial#,username from v$session where username is not null;
       SID    SERIAL# USERNAME
---------- ---------- ------------------------------
         8       2041 SYS
         9        437 EYGLE

执行跟踪。

SQL> exec dbms_system.set_ev(9,437,10046,8,'');
PL/SQL procedure successfully completed.

结束跟踪。

SQL> exec dbms_system.set_ev(9,437,10046,0,'');
PL/SQL procedure successfully completed.

基于会话级别跟踪产生的文件,可以通过查询V$DIAG_INFO视图,找到跟踪文件的名称和位置信息,查看其中的内容。
以下通过一个简单的测试来看看10046事件的作用。

SQL> create table eygle as select * from dba_objects;
SQL> select file_id,block_id,blocks from dba_extents where segment_name='EYGLE';

   FILE_ID   BLOCK_ID     BLOCKS
---------- ---------- ----------
         1      21601          8
         1      21609          8
         1      21617          8
         1      21625          8
         1      21633          8
         1      23433          8
         1      23441          8
         1      23449          8
         1      23457          8
         1      23465          8
SQL> alter session set events '10046 trace name context forever,level 12';
SQL> select count(*) from eygle; --表上无索引,所以此处引发一次全表扫描
  COUNT(*)
----------
      6207
SQL> alter session set events '10046 trace name context off';

检查一下Oracle生成的跟踪文件,比如关注一下等待事件 – db file scattered read (注意,自11g开始,由于Serial Table Scan的特性引入,你可能观察到全表扫描以Direct Path Read方式执行)。

 [oracle@eygle udump]$ cat rac1_ora_20695.trc |grep scatt
WAIT #1: nam='db file scattered read' ela= 11657 p1=1 p2=21602 p3=7
WAIT #1: nam='db file scattered read' ela= 1363 p1=1 p2=21609 p3=8
WAIT #1: nam='db file scattered read' ela= 1297 p1=1 p2=21617 p3=8
WAIT #1: nam='db file scattered read' ela= 1346 p1=1 p2=21625 p3=8
WAIT #1: nam='db file scattered read' ela= 1313 p1=1 p2=21633 p3=8
WAIT #1: nam='db file scattered read' ela= 6226 p1=1 p2=23433 p3=8
WAIT #1: nam='db file scattered read' ela= 1316 p1=1 p2=23441 p3=8
WAIT #1: nam='db file scattered read' ela= 1355 p1=1 p2=23449 p3=8
WAIT #1: nam='db file scattered read' ela= 1320 p1=1 p2=23457 p3=8
WAIT #1: nam='db file scattered read' ela= 884 p1=1 p2=23465 p3=5

大家注意这里的等待事件’db file scattered read’,意味着这里使用了全表扫描来访问数据.其中 p1,p2,p3分别代表了文件号、起始数据块块号和读取数据块的数量。
各参数的含义也可以从v$event_name视图中获得。

SQL> select name,PARAMETER1 p1,PARAMETER2 p2,PARAMETER3 p3 
  2  from v$event_name where name='db file scattered read';
NAME                     P1         P2         P3
-------------------------------------- ---------- ---------- ----------
db file scattered read         file#      block#     blocks

在数据库内部,这些等待时间最后都会累计到v$system_event动态性能视图中,是数据库性能诊断的一个重要参考。

SQL> select event,time_waited from v$system_event
  2  where event='db file scattered read';
EVENT                                     TIME_WAITED
---------------------------------------------------------------- --------
db file scattered read                               51

这里我们有必要提到另外一个相关的初始化参数db_file_multiblock_read_count,这个参数代表Oracle在执行全表扫描时每次IO操作可以读取的数据块的数量。
在前面的测试中,db_file_multiblock_read_count参数设置为16,由于extent大小为8个block,Oracle的一次IO操作不能跨越extent,所以前面的全表扫描每次只能读取8个block,进行了10次IO读取。
看一下进一步的测试。

SQL> create tablespace eygle datafile '/dev/raw/raw2' size 100M
  2  extent management local uniform size 256K;
SQL> alter table eygle move tablespace eygle;
SQL> select file_id,block_id,blocks from dba_extents where segment_name='EYGLE';
   FILE_ID   BLOCK_ID     BLOCKS
---------- ---------- ----------
         4          9         32
         4         41         32
         4         73         32
SQL> show parameter read_count
NAME                                 TYPE        VALUE
------------------------------------ ----------- -------------------------
db_file_multiblock_read_count              integer       16
SQL> alter session set events '10046 trace name context forever,level 12';
SQL> select count(*) from eygle;
  COUNT(*)
----------
      6207	
SQL> alter session set events '10046 trace name context off';
SQL> select value TRACE_FILE from v$diag_info where name='Default Trace File';
TRACE_FILE
------------------------------------------------------------------------
/opt/oracle/admin/rac/udump/rac1_ora_20912.trc

[oracle]$ cat /opt/oracle/admin/rac/udump/rac1_ora_20912.trc |grep scatt
WAIT #1: nam='db file scattered read' ela= 12170 p1=4 p2=10 p3=16
WAIT #1: nam='db file scattered read' ela= 2316 p1=4 p2=26 p3=15
WAIT #1: nam='db file scattered read' ela= 2454 p1=4 p2=41 p3=16
WAIT #1: nam='db file scattered read' ela= 2449 p1=4 p2=57 p3=16
WAIT #1: nam='db file scattered read' ela= 2027 p1=4 p2=73 p3=13

可以看到此时Oracle只需要5次IO操作就完成了全表扫描。通常较大的db_file_multiblock_ read_count设置可以加快全表扫描的执行。但是需要注意的是,增大db_file_multiblock_read_ count参数的设置,会使全表扫描的成本降低,在CBO优化器下可能会使Oracle更倾向于使用全表扫描而不是索引访问,一般建议使用默认值。

通过跟踪理解数据库的初始化
在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)

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

SQL> select * from bootstrap$ where rownum <5;
 LINE#  OBJ# SQL_TEXT
------ ----- ----------------------------------------------------------------------
    -1    -1 8.0.0.0.0
     0     0 CREATE ROLLBACK SEGMENT SYSTEM STORAGE (  INITIAL 112K NEXT 1024K MINE
             XTENTS 1 MAXEXTENTS 32765 OBJNO 0 EXTENTS (FILE 1 BLOCK 9))
     8     8 CREATE CLUSTER C_FILE#_BLOCK#("TS#" NUMBER,"SEGFILE#" NUMBER,"SEGBLOCK
             #" NUMBER) PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS 255 STORAGE (  IN
             ITIAL 24K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0
             OBJNO 8 EXTENTS (FILE 1 BLOCK 73)) SIZE 225
     9     9 CREATE INDEX I_FILE#_BLOCK# ON CLUSTER C_FILE#_BLOCK# PCTFREE 10 INITR
             ANS 2 MAXTRANS 255 STORAGE (  INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXE
             XTENTS 2147483645 PCTINCREASE 0 OBJNO 9 EXTENTS (FILE 1 BLOCK 81))

以上输出只显示了表中的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

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

(gdb) c
Continuing.
Breakpoint 2, 0x00000001006c78b4 in kqlobjlod ()

ADDRESS 	 CACHE_NAME	      E  LOCK_MODE SADDR	    KEYSTR
---------------- ----------------------- ----------------------------------------
00000001942E30D8 dc_tablespaces       N  0 00    0000000000000000000000000000000000000000
00000001942DE9B8 dc_rollback_segments Y  0 00    0000000000000000000000000000000000000000
00000001942E9080 dc_objects	          Y  0 00    000000000A00424F4F5453545241502400000000
00000001942DE2D0 dc_objects           N  3 0000000194782EB0 000000000600435F4F424A230000000000000000
00000001942E3340 dc_object_ids       Y  0 00    3800000000000000000000000000000000000000

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

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

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

BUCKET 37:
  row cache parent object: address=0x1942de9b8 cid=3(dc_rollback_segments)
  hash=5fed2a24 typ=9 transaction=0x0 flags=000000a6
  own=0x1942dea88[0x1942dea88,0x1942dea88] wat=0x1942dea98[0x1942dea98,0x1942dea98] mode=N
  status=VALID/INSERT/-/FIXED/-/-/-/-/-
  data=
  00000000 00000000 00000001 00000009 59530006 4d455453 00000000 00000000 
  00000000 00000000 00000000 00000000 00000003 00000000 00000000 00000000 
  00000000 00000000 00000000 00000000 
  BUCKET 37 total object count=1
ROW CACHE HASH TABLE: cid=4 ht=0x192b2e8a8 size=256

其中53595354454d正是SYSTEM回滚段。

SQL> select dump('SYSTEM',16) from dual;
DUMP('SYSTEM',16)
-------------------------------
Typ=96 Len=6: 53,59,53,54,45,4d

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

BUCKET 43170:
  row cache parent object: address=0x1942e9080 cid=8(dc_objects)
  hash=f3d1a8a1 typ=11 transaction=0x0 flags=000000a6
  own=0x1942e9150[0x1942e9150,0x1942e9150] wat=0x1942e9160[0x1942e9160,0x1942e9160] mode=N
  status=VALID/INSERT/-/FIXED/-/-/-/-/-
  set=0, complete=TRUE
  data=
  00000000 4f42000a 5453544f 24504152 00000000 00000000 00000000 00000000 
  00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
  00000000 00000000 00000000 00000038 00000001 00000038 066f7802 030d1411 
  11066f78 78030d14 1411066f 0001030d 00000000 00000000 00000000 00000000 
  00000000 00000000 
  BUCKET 43170 total object count=1

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

「喜欢文章,快来给作者赞赏墨值吧」

评论

0
1
墨值排行
今日本周综合
近期活动
全部
相关课程
全部