——深入了解Oracle数据字典,提高自学习Oracle能力
摘要:
我们知道Oracle通过数据字典来管理和展现数据库信息,这些信息至关重要。正确理解这部分内容有助于加强我们的oracle学习能力。
本文对Oracle数据字典的关系进行探讨。
接下来我们介绍一下怎样通过数据库本身来学习研究数据库。
首先,Oracle的字典表和视图基本上可以分为三个层次。
1.1 X$表
这一部分表是Oracle数据库的运行基础,在数据库启动时由Oracle应用程序动态创建。
这部分表对数据库来说至关重要,所以Oracle不允许SYSDBA之外的用户直接访问,显示授权不被允许。
如果显示授权你会收到如下错误:
SQL> grant select on x$ksppi to eygle;
grant select on x$ksppi to eygle
*
ERROR at line 1:
ORA-02030: can only select from fixed tables/views
Oracle的解释是:
ORA-02030 can only select from fixed tables/views
Cause: An attempt is being made to perform an operation other than a retrieval from a fixed table/view.
Action: You may only select rows from fixed tables/views.
一句话,这些对象你只能查询。
1.2 GV$和V$视图
在创建了X$表之后,Oracle创建了GV$和V$视图。 从Oracle8开始,GV$视图开始被引入,其含义为Global V$。
除了一些特例以外,每个V$视图都有一个对应的GV$视图存在。
GV$视图的产生是为了满足OPS环境的需要,在OPS环境中,查询GV$视图返回所有实例信息,而每个V$视图是基于GV$视图,增加了INST_ID列的WHERE条件限制建立,只包含当前连接实例信息。
注意,每个V$视图都包含类似语句:
where inst_id = USERENV('Instance')
用于限制返回当前实例信息。
我们从GV$FIXED_TABLE和V$FIXED_TABLE开始,看一下GV$视图和V$视图的结构:
SQL> select view_definition from v$fixed_view_definition where view_name='V$FIXED_TABLE';
VIEW_DEFINITION
------------------------------------------------------------------------------------------------------
select NAME , OBJECT_ID , TYPE , TABLE_NUM from GV$FIXED_TABLE where inst_id = USERENV('Instance')
这里我们看到V$FIXED_TABLE基于GV$FIXED_TABLE创建。
SQL> select view_definition from v$fixed_view_definition where view_name='GV$FIXED_TABLE';
VIEW_DEFINITION
--------------------------------------------------------------------------------------------------------
select inst_id,kqftanam, kqftaobj, 'TABLE', indx from x$kqfta
union all
select inst_id,kqfvinam, kqfviobj, 'VIEW', 65537 from x$kqfvi
union all
select inst_id,kqfdtnam, kqfdtobj, 'TABLE', 65537 from x$kqfdt
这样我们找到了GV$FIXED_TABLE视图的创建语句,该视图基于X$表创建。
我们知道,GV$视图和V$视图是在数据库创建过程中建立起来的,内置于数据库中,Oracle通过v$fixed_view_definition视图为我们展现这些定义。
1.3 GV_$,V_$视图和V$,GV$同义词
在GV$和V$之后,Oracle建立了GV_$和V_$视图,随后为这些视图建立了公用同义词。这些工作都是通过catalog.sql脚本实现的。
我们从catalog.sql脚本中摘录一段:
create or replace view v_$fixed_table as select * from v$fixed_table;
create or replace public synonym v$fixed_table for v_$fixed_table;
create or replace view gv_$fixed_table as select * from gv$fixed_table;
create or replace public synonym gv$fixed_table for gv_$fixed_table;
从以上脚本中,我们注意到,第一个视图V_$和GV_$视图基于V$和GV$视图首先被创建,然后基于V_$和GV_$视图的同义词被创建。
通过V_$视图,Oracle把V$视图和普通用户隔离,V_$视图的权限可以授予其他用户,而Oracle不允许任何对于V$视图的直接授权,我们看以下例子:
[oracle@jumper udump]$ sqlplus '/ as sysdba'
SQL*Plus: Release 9.2.0.4.0 - Production on Mon Jun 13 16:41:41 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning option
JServer Release 9.2.0.4.0 - Production
SQL> grant select on v$sga to eygle;
grant select on v$sga to eygle
*
ERROR at line 1:
ORA-02030: can only select from fixed tables/views
SQL> grant select on v_$sga to eygle;
Grant succeeded.
对于内部X$表及V$视图的限制,我猜测是通过软件代码实现的,而并非通过数据库权限控制。
所以,实际上通常我们大部分用户访问的V$对象,并不是视图,而是指向V_$视图的同义词;而V_$视图是基于真正的V$视图(这个视图是基于X$表建立的)。
在进行数据访问时,Oracle访问VIEW优先,然后是同义词。我们通过以下实验来验证一下这个结论。
首先参考Oracle处理机制,创建X$EYGLE,V$EYGLE,V_$EYGLE和公用同义词V$EYGLE:
[oracle@jumper udump]$ sqlplus eygle/eygle
SQL*Plus: Release 9.2.0.4.0 - Production on Mon Jun 13 17:37:25 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning option
JServer Release 9.2.0.4.0 - Production
SQL> create table x$eygle as select username from dba_users;
Table created.
SQL> create view v$eygle as select * from x$eygle;
View created.
SQL> create view v_$eygle as select * from v$eygle;
View created.
SQL> create public synonym v$eygle for v_$eygle;
Synonym created.
然后我们在sys用户下创建V$EYGLE视图:
SQL> connect / as sysdba
Connected.
SQL> create view v$eygle as select username,user_id from dba_users;
View created.
此时查询,我们得到的SYS的V$EYGLE信息:
SQL> desc v$eygle;
Name Null? Type
-----------------------------------------------------------------
USERNAME NOT NULL VARCHAR2(30)
USER_ID NOT NULL NUMBER
当我们删除这个视图以后,再次访问时,Oracle选择访问了V$EYGLE同义词:
SQL> drop view v$eygle ;
View dropped.
SQL> desc v$eygle
Name Null? Type
-----------------------------------------------------------------
USERNAME NOT NULL VARCHAR2(30)
SQL>
v$fixed_view_definition视图是我们研究Oracle对象关系的一个入口,仔细理解Oracle的数据字典机制,有助于深入了解和学习Oracle数据库知识。
1.4 再进一步
1.4.1 数据库的初始化
首先我们考察bootstrap$表,该表中记录了数据库启动的基本及驱动信息。
SQL> col line# for 99
SQL> col obj# for 99
SQL> select * from bootstrap$ order by line#;
LI OB SQL_TEXT
-- -- --------------------------------------------------------------------------------
-1 -1 8.0.0.0.0
0 0 CREATE ROLLBACK SEGMENT SYSTEM STORAGE ( INITIAL 112K NEXT 1024K MINEXTENTS 1 M
2 2 CREATE CLUSTER C_OBJ#("OBJ#" NUMBER) PCTFREE 5 PCTUSED 40 INITRANS 2 MAXTRANS 25
3 3 CREATE INDEX I_OBJ# ON CLUSTER C_OBJ# PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE
4 4 CREATE TABLE TAB$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOT NULL
5 5 CREATE TABLE CLU$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOT NULL
6 6 CREATE CLUSTER C_TS#("TS#" NUMBER) PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS 255
7 7 CREATE INDEX I_TS# ON CLUSTER C_TS# PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE (
8 8 CREATE CLUSTER C_FILE#_BLOCK#("TS#" NUMBER,"SEGFILE#" NUMBER,"SEGBLOCK#" NUMBER)
9 9 CREATE INDEX I_FILE#_BLOCK# ON CLUSTER C_FILE#_BLOCK# PCTFREE 10 INITRANS 2 MAXT
10 10 CREATE CLUSTER C_USER#("USER#" NUMBER) PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS
11 11 CREATE INDEX I_USER# ON CLUSTER C_USER# PCTFREE 10 INITRANS 2 MAXTRANS 255 STORA
12 12 CREATE TABLE FET$("TS#" NUMBER NOT NULL,"FILE#" NUMBER NOT NULL,"BLOCK#" NUMBER
13 13 CREATE TABLE UET$("SEGFILE#" NUMBER NOT NULL,"SEGBLOCK#" NUMBER NOT NULL,"EXT#"
14 14 CREATE TABLE SEG$("FILE#" NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBE
15 15 CREATE TABLE UNDO$("US#" NUMBER NOT NULL,"NAME" VARCHAR2(30) NOT NULL,"USER#" NU
16 16 CREATE TABLE TS$("TS#" NUMBER NOT NULL,"NAME" VARCHAR2(30) NOT NULL,"OWNER#" NUM
17 17 CREATE TABLE FILE$("FILE#" NUMBER NOT NULL,"STATUS$" NUMBER NOT NULL,"BLOCKS" NU
18 18 CREATE TABLE OBJ$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"OWNER#" NUMBER NOT N
19 19 CREATE TABLE IND$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOT NULL
....
这部分信息,在数据库启动时最先被加载,跟踪数据库的启动过程,我们发现数据库启动的第一个动作就是:
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 377))
这部分代码是写在Oracle应用程序中的,在内存中创建了bootstrap$以后,Oracle就可以从file 1,block 377上读取其他信息,创建重要的数据库对象。从而根据这一部分信息启动数据库,这就实现了数据库的引导,类似于操作系统的初始化。
这部分你可以参考biti_rainy在Itpub上的文章(
http://www.itpub.net/199099.html)。
Oracle的X$表信息可以从v$fixed_table中查到:
SQL> select count(*) from v$fixed_table where name like 'X$%';
COUNT(*)
----------
394
对于Oracle9iR2,共有394个X$对象被记录。
1.4.2 GV$和V$视图
X$表建立以后,基于X$表的GV$和V$视图得以创建。
这部分视图我们也可以通过查询V$FIXED_TABLE得到。
SQL> select count(*) from v$fixed_table where name like 'GV$%';
COUNT(*)
----------
259
这一部分共259个对象。
SQL> select count(*) from v$fixed_table where name like 'V$%';
COUNT(*)
----------
259
同样是259个对象。
v$fixed_table共记录了:
394 + 259 + 259 共 912 个对象。
SQL> select count(*) from v$fixed_table;
COUNT(*)
----------
912
以上是Oracle9iR2单机环境中的数据:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for Linux: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production
1.5 最后的验证
最后然我们通过V$PARAMETER视图来追踪一下数据库的架构:
1.5.1 V$PARAMETER的结构
SQL> select view_definition from v$fixed_view_definition a where a.VIEW_NAME='V$PARAMETER';
VIEW_DEFINITION
--------------------------------------------------------------------------------
select NUM , NAME , TYPE , VALUE , ISDEFAULT , ISSES_MODIFIABLE , ISSYS_MODIFIA
BLE , ISMODIFIED , ISADJUSTED , DESCRIPTION, UPDATE_COMMENT from GV$PARAMETER wh
ere inst_id = USERENV('Instance')
我们看到V$PARAMETER是由GV$PARAMETER创建的,GV$PARAMETER则是由X$创建的。
SQL> select view_definition from v$fixed_view_definition a where a.VIEW_NAME='GV$PARAMETER';
VIEW_DEFINITION
--------------------------------------------------------------------------------
select x.inst_id,x.indx+1,ksppinm,ksppity,ksppstvl,ksppstdf, decode(bitand(kspp
iflg/256,1),1,'TRUE','FALSE'), decode(bitand(ksppiflg/65536,3),1,'IMMEDIATE',2,
'DEFERRED', 3,'IMMEDIATE','FALSE'), decode(bit
and(ksppstvf,7),1,'MODIFIED',4,'SYSTEM_MOD','FALSE'), decode(bitand(ksppstvf,2)
,2,'TRUE','FALSE'), ksppdesc, ksppstcmnt from x$ksppi x, x$ksppcv y where (x.i
ndx = y.indx) and ((translate(ksppinm,'_','#') not like '#%') or (ksppstdf = 'F
ALSE'))
说明:在这里我们看到GV$PARAMETER来源于x$ksppi,x$ksppcv两个X$表。 x$ksppi,x$ksppcv 基本上包含所有数据库参数,v$parameter展现的是不包含"_"开头的参数。以"_"开头的参数我们通常称为隐含参数,一般不建议修改,但很多因为功能强大经常使用而广为人知。
1.5.2 视图还是同义词
在非SYS用户下查询,很多朋友曾经提出过疑问,那就是,当我访问V$PARAMETER对象时,访问的是视图还是同义词?
如果你还记得我们前面讲过的内容,那么你会知道,毫无疑问,这里访问的是同义词,因为除了SYS用户以外,其他用户不能查询V$视图,V$视图也不能被授权给其他用户。
那么这个问题实际上是不成立的。
SQL> connect / as sysdba
Connected.
SQL> grant select on v$parameter to eygle;
grant select on v$parameter to eygle
*
ERROR at line 1:
ORA-02030: can only select from fixed tables/views
SQL> connect eygle/eygle
Connected.
SQL> desc sys.v$parameter
ERROR:
ORA-04043: object sys.v$parameter does not exist
SQL> desc v$parameter
Name Null? Type
-----------------------------------------------------------------
NUM NUMBER
NAME VARCHAR2(64)
TYPE NUMBER
VALUE VARCHAR2(512)
ISDEFAULT VARCHAR2(9)
ISSES_MODIFIABLE VARCHAR2(5)
ISSYS_MODIFIABLE VARCHAR2(9)
ISMODIFIED VARCHAR2(10)
ISADJUSTED VARCHAR2(5)
DESCRIPTION VARCHAR2(64)
UPDATE_COMMENT VARCHAR2(255)
1.5.3 Oracle如何通过同义词定位对象
如果愿意的话,我们可以进一步来进行追溯,使用10046事件,我们可以看到更多的东西。
通过10046事件跟踪查询:
[oracle@jumper udump]$ sqlplus eygle/eygle
SQL*Plus: Release 9.2.0.4.0 - Production on Mon Jun 13 18:29:22 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning option
JServer Release 9.2.0.4.0 - Production
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered.
SQL> select count(*) from v$parameter;
COUNT(*)
----------
262
SQL> exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning option
JServer Release 9.2.0.4.0 - Production
10046事件的使用请参考:
Ok,在这里我们不要使用tkprof格式化,因为tkprof可能会隐去重要信息(本文仅摘取几段重要跟踪信息,你完全可以通过实验获得相同的输出):
第一段重要代码是:
PARSING IN CURSOR #2 len=198 dep=1 uid=0 oct=3 lid=0 tim=1092440257023120 hv=2703824309 ad='567681f0'
select obj#,type#,ctime,mtime,stime,status,dataobj#,flags,oid$, spare1, spare2 from obj$ where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null
END OF STMT
PARSE #2:c=0,e=1601,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=1092440257023088
BINDS #2:
bind 0: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=08 oacfl2=1 size=24 offset=0
bfp=b701cf24 bln=22 avl=02 flg=05
value=25
bind 1: dty=1 mxl=32(11) mal=00 scl=00 pre=00 oacflg=18 oacfl2=1 size=32 offset=0
bfp=b701c7b4 bln=32 avl=11 flg=05
value="V$PARAMETER"
bind 2: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=08 oacfl2=1 size=24 offset=0
bfp=b701c790 bln=24 avl=02 flg=05
value=1
Oracle根据三个传入参数owner#=25,name=V$PARAMETER,namespace=1,来判断对象类型,按照表、视图优先规则来定位判断,对于本例这个查询是不会有结果的。
接下来Oracle继续判断,那么此时需要验证同一词了:
PARSING IN CURSOR #4 len=46 dep=1 uid=0 oct=3 lid=0 tim=1092440257028409 hv=3378994511 ad='576eb040'
select node,owner,name from syn$ where obj#=:1
END OF STMT
PARSE #4:c=0,e=1278,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=1092440257028379
BINDS #4:
bind 0: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=08 oacfl2=1 size=24 offset=0
bfp=b701b3cc bln=22 avl=03 flg=05
value=841
传入绑定变量值是841,我们看看841是什么:
SQL> select object_name,object_id,object_type from dba_objects where object_id=841;
OBJECT_NAME OBJECT_ID OBJECT_TYPE
------------------------------ ---------- ------------------
V$PARAMETER 841 SYNONYM
841正是这个同义词,我们再继续看这个递归SQL的作用:
SQL> select node,owner,name from syn$ where obj#=841;
NODE OWNER NAME
-------- ------------------------------ ------------------------------
SYS V_$PARAMETER
原来这个SQL获得的是同义词的底层对象,这里得到了V_$PARAMETER。
我们继续向下看:
PARSING IN CURSOR #8 len=37 dep=1 uid=0 oct=3 lid=0 tim=1092440257074273 hv=3468666020 ad='576db210'
select text from view$ where rowid=:1
END OF STMT
PARSE #8:c=0,e=1214,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=1092440257074242
BINDS #8:
bind 0: dty=11 mxl=16(16) mal=00 scl=00 pre=00 oacflg=18 oacfl2=1 size=16 offset=0
bfp=b7018770 bln=16 avl=16 flg=05
value=000001CD.0013.0001
EXEC #8:c=0,e=972,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1092440257075602
注意这里,Oracle执行查询访问view$视图,获得视图定义文本,我们看一下这里访问的是什么对象,绑定变量传入的rowid值为000001CD.0013.0001,注意这是个受限rowid,查询时需要转换一下处理:
SQL> select obj# from view$ where dbms_rowid.rowid_to_restricted(rowid,0) = '000001CD.0013.0001';
OBJ#
----------
840
SQL> select object_name,object_type from dba_objects where object_id=840;
OBJECT_NAME OBJECT_TYPE
------------------------------ ------------------
V_$PARAMETER VIEW
这里Oracle访问的正是V_$PARAMETER视图的定义方式。执行查询可以得到:
select text from view$ where obj#=840;
TEXT
--------------------------------------------------------------------------------
select "NUM","NAME","TYPE","VALUE","ISDEFAULT","ISSES_MODIFIABLE","ISSYS_MODIFIABLE","ISMODIFIED","ISADJUSTED","DESCRIPTION","UPDATE_COMMENT" from v$parameter
至此就完成了查询中的回溯及定位,当然,实际过程中Oracle后台的递归操作比这还要复杂的多,感兴趣的朋友可以按照文中的方法测试研究一下,文中不再赘述。
参考文献:
使用SQL_TRACE进行数据库诊断
Oracle数据库创建脚本 sql.bsq文件
关于数据库open的深入探究
http://www.itpub.net/199099.html