Archive

Posts Tagged ‘index’

Oracle 9i新特性研究5监视未使用索引

March 5th, 2009 No comments

Oracle 9i新特性研究   五 监视未使用索引

 

索引可以加快查询的速度,但索引会占用许多存储空间,在插入和删除行的时候,索引还会引入额外的开销,因此确保索引得到有效利用是我们很关注的一个问题。在Oracle9i之前,要知道一个索引是否被使用是困难的,而Oracle 9i中提供了一个有效的监控方法:ALTER INDEX MONITORING USAGE。下面我讲详 细说明如何使用该方法来鉴别未使用的索引。

一、我们先通过一个例子具体说明“ALTER INDEX MONITORING USAGE”的使用方法:

1、建测试表

create table test(id number(3),name varchar2(10));

insert into test values(1,’aaaaaaaa’);

insert into test values(2,’www.ncn.cn’);

insert into test values(3,’aadfaaaa’);

insert into test values(4,’gototop’);

insert into test values(5,’shenzhen’);

insert into test values(6,’china’);

commit;

alter table test add (constraint test_pk primary key (id));

2、查询v$object_usage(因为没有监视,所以还看不到内容)

column index_name format a12

column monitoring format a10

column used format a4

column start_monitoring format a19

column end_monitoring format a19

SQL>select index_name,monitoring,used,start_monitoring,end_monitoring from v$object_usage;

no rows selected

Elapsed: 00:00:00.00

3、开始监控索引的使用情况

SQL> alter index test_pk monitoring usage;

Index altered.

Elapsed: 00:00:00.05

4、查询v$object_usage(可以看到正监视中)

SQL> select index_name,monitoring,used,start_monitoring,end_monitoring from v$object_usage;

INDEX_NAME MONITORING USED START_MONITORING END_MONITORING

———— ———- —- ——————- ——————-

TEST_PK YES NO 05/15/2003 13:28:22

Elapsed: 00:00:00.00

5、使用索引进行查询

SQL> set autotrace on explain

SQL> select * from test where id = 2;

ID NAME

———- ———-

2 www.ncn.cn

Elapsed: 00:00:00.00

Execution Plan

———————————————————-

0 SELECT STATEMENT Optimizer=CHOOSE

1 0 TABLE ACCESS (BY INDEX ROWID) OF ‘TEST’

2 1 INDEX (UNIQUE SCAN) OF ‘TEST_PK’ (UNIQUE)

SQL> set autotrace off

SQL> /

ID NAME

———- ———-

2 www.ncn.cn

Elapsed: 00:00:00.00

SQL>

从上我们可以看到确实使用了索引。

6、查询v$object_usage(可以看到索引被使用过,但目前还处于被监视过程中)

SQL> select index_name,monitoring,used,start_monitoring,end_monitoring from v$object_usage;

INDEX_NAME MONITORING USED START_MONITORING END_MONITORING

———— ———- —- ——————- ——————-

TEST_PK YES YES 05/15/2003 13:28:22

Elapsed: 00:00:00.00

7、停止监视,并查询v$object_usage

SQL> alter index test_pk nomonitoring usage;

Index altered.

Elapsed: 00:00:05.03

SQL> select index_name,monitoring,used,start_monitoring,end_monitoring from v$object_usage;

INDEX_NAME MONITORING USED START_MONITORING END_MONITORING

———— ———- —- ——————- ——————-

TEST_PK NO YES 05/15/2003 13:28:22 05/15/2003 13:40:00

Elapsed: 00:00:00.64

到此为止,监视结束,MONITORING为NO,END_MONITORING给出了时间戳。

二、v$object_usage视图解释

从上面的例子中我们可以看出,索引的监视信息都是存在在v$objec_usage视图中,该视图的定义如下:

v$object_usage是基于以下基表建立起来的:

create or replace view v$object_usage

(index_name, table_name, monitoring, used, start_monitoring, end_monitoring)

as

select io.name, t.name,

decode(bitand(i.flags, 65536), 0, ‘NO’, ‘YES’),

decode(bitand(ou.flags, 1), 0, ‘NO’, ‘YES’),

ou.start_monitoring,

ou.end_monitoring

from sys.obj$ io, sys.obj$ t, sys.ind$ i, sys.object_usage ou

where io.owner# = userenv(‘SCHEMAID’)

and i.obj# = ou.obj#

and io.obj# = ou.obj#

and t.obj# = i.bo#

/

下面是该视图列的描述:

INDEX_NAME: sys.obj$.name 中的索引名字

TABLE_NAME: sys.obj$obj$name 中的表名

MONITORING: YES (索引正在被监控), NO (索引没有被监控)

USED: YES (索引已经被使用过), NO (索引没有被使用过)

START_MONITORING: 开始监控的时间

END_MONITORING: 结束监控的时间

所有被使用过至少一次的索引都可以被监控并显示到这个视图中。

三、监视数据库中所有索引的使用情况

1、生成开始/结束监视索引的SQL脚本:

set heading off

set echo off

set feedback off

set pages 10000

spool start_index_monitor.sql

select ‘alter index ‘||owner||’.'||index_name||’ monitoring usage;’   from dba_indexes
where owner in (‘YOUR’,'PROD_DB’,'OWNER’,'LIST’);

spool off

set heading on

set echo on

set feedback on

 

2、进行监视并查询结果:

在业务量比较多的一天上班时运行start_index_monitor.sql,下班前运行stop_index_monitor.sql,之后就可以在各用户自己的v$object_usage视图中看到该SCHEMA下的索引使用情况了:

SQL> conn t/t

Connected.

SQL> select index_name,table_name,used

2 from v$object_usage

3 where used=’NO’;

INDEX_NAME TABLE_NAME USED

———— —————————— —-

TEST_PK TEST NO

1 row selected.

SQL>

3、改进结果查寻方法

v$object_usage是基于以下基表建立起来的:

create or replace view v$object_usage

(index_name, table_name, monitoring, used, start_monitoring, end_monitoring)

as

select io.name, t.name,

decode(bitand(i.flags, 65536), 0, ‘NO’, ‘YES’),

decode(bitand(ou.flags, 1), 0, ‘NO’, ‘YES’),

ou.start_monitoring,

ou.end_monitoring

from sys.obj$ io, sys.obj$ t, sys.ind$ i, sys.object_usage ou

where io.owner# = userenv(‘SCHEMAID’)

and i.obj# = ou.obj#

and io.obj# = ou.obj#

and t.obj# = i.bo#

/

注意到v$object_usage关键信息来源于OBJECT_USAGE表.另外我们可以注意一下,此处v$object_usage的查询基于userenv(‘SCHEMAID’)建立.所以以不同用户登录,你是无法看到其他用户的索引监视信息的,即使是dba,但是可以从object_usage表中得到.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

你也许已经注意到,上面查询结果是需要我们单独查询各SCHEMA中的v$object_usage,其实我们可以通过给v$object_usage视图添加一个owner列来创建一个可以存储所有SHCEMA的v$object_usage视图,不妨叫做v$all_object_usage,定义如下:

CREATE OR REPLACE VIEW SYS.V$ALL_OBJECT_USAGE
(
OWNER,
INDEX_NAME,
TABLE_NAME,
MONITORING,
USED,
START_MONITORING,
END_MONITORING
)
AS
select u.name, io.name, t.name,
decode(bitand(i.flags, 65536), 0, ‘NO’, ‘YES’),
decode(bitand(ou.flags, 1), 0, ‘NO’, ‘YES’),
ou.start_monitoring,
ou.end_monitoring
from sys.obj$ io, sys.obj$ t, sys.ind$ i, sys.object_usage ou, sys.user$ u
where i.obj# = ou.obj#
and io.obj# = ou.obj#
and t.obj# = i.bo#
and io.owner# = u.user#;
COMMENT ON TABLE SYS.V$ALL_OBJECT_USAGE IS ‘Record of all index usage’;
GRANT SELECT ON SYS.V$ALL_OBJECT_USAGE TO “PUBLIC”;
CREATE PUBLIC SYNONYM V$ALL_OBJECT_USAGE FOR SYS.V$ALL_OBJECT_USAGE;

之后就可以从这个视图中查询相关信息了,下面是示例过程:

SQL> conn sys/sys as sysdba

Connected.

SQL> desc v$all_object_useage

ERROR:

ORA-04043: object v$all_object_useage does not exist

SQL> @/oracle/oracle9/cyx/all_object_usage.sql

 

SQL> @start_index_monitor.sql

SQL> alter index T.INDEX_CFS monitoring usage;

Index altered.

SQL> alter index T.TEST_PK monitoring usage;

Index altered.

SQL> conn t/t

Connected.

SQL> select * from test where id <5;

ID NAME

———- ———-

1 aaaaaaaa

2 www.ncn.cn

3 aadfaaaa

4 gototop

4 rows selected.

SQL> conn sys/sys as sysdba

Connected.

SQL> @stop_index_monitor.sql

SQL> alter index T.INDEX_CFS nomonitoring usage;

Index altered.

SQL> alter index T.TEST_PK nomonitoring usage;

Index altered.

SQL> @report_index_usage.sql

SQL> set pages 10000

SQL> set pagesize 200

SQL> set linesize 100

SQL> spool report_index_usage.txt

SQL> ttitle center “–Report of Unused Indexes–”

SQL> select owner,table_name,index_name,used

2 from v$all_object_usage

3 where used=’NO’;

–Report of Unused Indexes–

OWNER TABLE_NAME INDEX_NAME USED

———— ————– ———— —-

T CFS INDEX_CFS NO

1 row selected.

SQL> spool off

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Oracle9i的Bug

在9205之前,如果你不慎监控了SYS.I_OBJAUTH1索引,并且不幸在重起数据库之前没有停止它,那么你的数据库将会无法启动,并且不会给出任何错误信息。

以下这条简单的语句可以轻易再现这个问题:

‘ALTER INDEX SYS.I_OBJAUTH1 MONITORING USAGE’

如果你有了足够好的备份(严重警告,请不要拿你的生产数据库进行测试),你可以尝试一下:

[oracle@jumper oradata]$ sqlplus “/ as sysdba”SQL*Plus: Release 9.2.0.4.0 – Production on Sat Dec 4 10:09:30 2004

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 index SYS.I_OBJAUTH1 monitoring usage ;

Index altered.

SQL> shutdown immediate;

Database closed.

Database dismounted.

ORACLE instance shut down.

SQL> startup

ORACLE instance started.

Total System Global Area 80811208 bytes

Fixed Size 451784 bytes

Variable Size 37748736 bytes

Database Buffers 41943040 bytes

Redo Buffers 667648 bytes

Database mounted.

此时,数据库挂起,而且不会有任何提示,在alert<sid>.log文件中,你可以看到:

[oracle@jumper bdump]$ tail -f alert_conner.log

Completed: ALTER DATABASE MOUNT

Sat Dec 4 10:09:49 2004

ALTER DATABASE OPEN

Sat Dec 4 10:09:49 2004

LGWR: Primary database is in CLUSTER CONSISTENT mode

Thread 1 opened at log sequence 54

Current log# 2 seq# 54 mem# 0: /opt/oracle/oradata/conner/redo02.log

Successful open of redo thread 1.

Sat Dec 4 10:09:49 2004

SMON: enabling cache recovery

Sat Dec 4 10:10:33 2004

Restarting dead background process QMN0

QMN0 started with pid=9

然后数据库将会停在此处。如果不知道此bug存在,你可能会一筹莫展的。现在你能做的就是从备份中恢复,或者升级。

[oracle@jumper oradata]$ rm -rf conner

[oracle@jumper oradata]$ cp -R connerbak/ conner

[oracle@jumper oradata]$ sqlplus ‘/ as sysdba’SQL*Plus: Release 9.2.0.4.0 – Production on Sat Dec 4 10:19:07 2004

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

Connected to an idle instance.

SQL> startup

ORACLE instance started.

Total System Global Area 80811208 bytes

Fixed Size 451784 bytes

Variable Size 37748736 bytes

Database Buffers 41943040 bytes

Redo Buffers 667648 bytes

Database mounted.

Database opened.

SQL>

在特殊的情况下,你可能需要清除这个v$object_usage视图中的信息.

Categories: index, oracle Tags: ,

由浅至深讲解Oracle数据库 B-tree索引

January 21st, 2009 No comments

假如聚簇因子过大,那么重建索引可能会有好处,聚簇因子应该接近块的数量,而非行的数量。

B-tree索引:

·索引会随着时间的增加而变的不平衡;

·删除的索引空间不会被重用;

·随着索引层数的增加,索引将会变得无效并需要重建;

·聚簇因子差,索引需要重建;

·为了提高性能,索引需要经常重建;

索引基础

·一个更新由一个删除和一个插入组成;

·页块由索引条目(row header(2/3B)|length(1B)|indexed data value(nB)|length(1B)|RowID(6B))和相应的rowid组成;

·每个页块包含两个指针分别前面的页块和后面页块;

Treedump

alter session set events ‘immediate trace name treedump level index_object_id’;

—– begin tree dump

branch: 0×424362 4342626 (0: nrow: 2, level: 1)

leaf: 0×424363 4342627 (-1: nrow: 540 rrow: 540)

leaf: 0×424364 4342628 (0: nrow: 461 rrow: 461)

—– end tree dump

以上dump包含的信息如下:

块类型:branch(分支块);leaf(页块);

块地址:0×424362 4342626;

nrow:索引条目的数量;

rrow:当前块中的索引条目数量;

level:分支块等级(页块隐示为0);

Block Dump

alter system dump datafile X block X;

alter system dump datafile X block min X1 block max X2

Start dump data blocks tsn: 0 file#: 1 minblk 148538 maxblk 148538

buffer tsn: 0 rdba: 0x0042443a (1/148538)

scn: 0×0000.00162a95 seq: 0×01 flg: 0×04 tail: 0x2a950601

frmt: 0×02 chkval: 0x8b5c type: 0×06=trans data

Block header dump: 0x0042443a

Object id on Block? Y

seg/obj: 0xd1fe csc: 0×00.162a95 itc: 2 flg: O typ: 2 – INDEX

fsl: 0 fnx: 0x42443b ver: 0×01

Itl Xid Uba Flag Lck Scn/Fsc

0×01 0×0005.02a.00000332 0x008005cb.020e.01 CB– 0 scn 0×0000.00162a92

0×02 0×0008.011.00000346 0x008002e6.0163.03 C— 0 scn 0×0000.00162a93

该dump包含的信息如下:

rdba:分支块的相对数据库块地址(文件号/块号);

scn:块最后改变的SCN号;

type:块类型;

seq:块改变的数量;

seg/obj: 16进制对象ID;

typ:段类型;

Itl:相关的事务槽(页块默认为2),包括槽ID,事务ID,撤销块地址,标记,锁信息,和事务SCN;

通过rba确定数据文件号和块号:

select DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(rba),

DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(rba)

from dual;

通用的索引块头

header address 153168988=0x9212c5c

kdxcolev 0

KDXCOLEV Flags = – - -

kdxcolok 1

kdxcoopc 0×89: opcode=9: iot flags=— is converted=Y

kdxconco 2

kdxcosdc 2

kdxconro 254

kdxcofbo 544=0×220

kdxcofeo 4482=0×1182

kdxcoavs 3938

kdxcolev:索引级别(0代表页块);

kdxcolok:标示结构块事块是否发生;

kdxcoopc:内部操作码;

kdxconco:索引列数量,包括ROWID;

kdxcosdc:块中索引结构改变的数量;

kdxconro:索引条目的数量,不包括kdxbrlmc指针;

kdxcofbo:块中空闲空间的开始位置;

kdxcofeo:块中空闲空间的结束位置;

kdxcoavs:块中的可用空间数量(kdxcofbo-kdxcofeo);

分支头区域

kdxbrlmc 8388627=0×800013

kdxbrsno 92

kdxbrbksz 8060

kdxbrlmc:如果索引值小于第一个值(row#0),则为该索引值所在的块地址;

kdxbrsno:最后更改的索引条目;

kdxbrbksz:可使用的块空间;

叶块头区域

kdxlespl 0

kdxlende 127

kdxlenxt 4342843=0x42443b

kdxleprv 4342845=0x42443d

kdxledsz 0

kdxlebksz 8036

kdxlespl:块拆分时被清除的未提交数据的字节数;

kdxlende:被删除的条目数;

kdxlenxt:下一个页块的RBA;

kdxleprv:上一个页块的RBA;

kdxlebksz:可使用的块空间(默认小于分支的可用空间);

分支条目

row#0[7898] dba: 4342821=0×424425

col 0; len 3; (3): c2 61 03

col 1; TERM

row#1[7214] dba: 4342873=0×424459

col 0; len 4; (4): c3 04 02 17

col 1; TERM

行号,[块中的起始位置] dba;

列号,列长度,列值;

brach中的每个entry有2个columns:

一个是child blocks中的最大值,另一个是指向的下一层block的address’

但是某些时候可能会有一些比较奇怪的结果:

row#0[7025] dba: 4342908=0x42447c

col 0; len 1024; (1024):

41 20 20 20 …20

col 1; len 4; (4): 00 42 44 73

—– end of branch block dump —–

叶条目

row#38[5014] flag: —-S-, lock: 2, len=14

col 0; len 4; (4): c3 04 61 55

col 1; len 6; (6): 00 42 43 db 00 a1

row#39[5028] flag: —DS-, lock: 2, len=14

行号[在块中的开始位置] 各种标记(锁信息,删除信息);

索引列号,长度,值。其中6个字节的为ROWID号,将其转换为二进制,算法结果为:

前10 bit代表了file_id

中22 bit代表了block_id

后16 bit代表了row_id;

通过文件号和块号算出的结果为创建该索引的表的块。

奇怪的是,为什么索引中的rowid不能直接找到obj_id?

因为索引段对应的数据段在 一开始就知道,因为是先知道数据段才找到索引段,然后

根据索引段内容去搜索数据段内容,所以索引段中 rowid 不必包含 data_object_id 信息。

如果索引是建立在非分区表上,或者是分区表上的 LOCAL 索引,使用的是6 bytes的 Restricted ROWID。如果索引是建立在分区表上的 GLOBAL index,则使用 10bytes 的 Extended ROWID,这样可以区分索引指向哪个分区表。

更新/重用索引条目

当更新了索引条目后,DUMP如下:

kdxconco 2

kdxcosdc 0

kdxconro 2

kdxcofbo 40=0×28

kdxcofeo 8006=0x1f46

kdxcoavs 7966

kdxlespl 0

kdxlende 1

kdxlenxt 0=0×0

kdxleprv 0=0×0

kdxledsz 0

kdxlebksz 8036

row#0[8021] flag: —D-, lock: 2 => deleted index entry

col 0; len 5; (5): 42 4f 57 49 45

col 1; len 6; (6): 00 80 05 0a 00 00

row#1[8006] flag: —–, lock: 2

col 0; len 5; (5): 5a 49 47 47 59 => new index entry

col 1; len 6; (6): 00 80 05 0a 00 00

更新后,将包含一个删除的条目,一个新的条目。在随后的插入中,如果新插入的索引条目能够放到被删除的索引条目的位置上,就会直接重用这个条目。根据索引值来决定。

所谓重用,是对row 的重用,而不是对row所在物理存储(或说物理位置)的重用。索引是按照indexed value对row进行排序的。有新的row被插入,首先按照value排序,将他放在合适的row list中,如果他的位置正好原来有个row被删掉了,则重用这个row在row list中的位置。至于物理存储上,则可能根据版本不同会有不同。在10.2中,我做的测试并没有向下开辟空间。

结论:

·到叶块中的任何插入都将移除所有被删除的条目;

·删除的空间在随后的写中被清除;

·删除的空间在延迟块清除中被清除;

·全空块被放在空闲列表,可以重用;

索引统计

·dba_indexes

·dbms_stats

·index_stats

– analyze index index_name validate structure;

–分析资源,锁;

·v$segment_statistics

statistics_level = typical (or all)

注意事项:

blevel (dba_indexes) vs. height (index_stats)

blocks allocated,但未必使用;

lf_rows_len包含行负载(单列索引12个字节)

pct_used索引结构中当前使用的空间:(used_space/btree_space)*100

绝大多数索引统计包含删除的条目:

non-deleted rows = lf_rows – del_lf_rows

pct_used by non-deleted rows = ((used_space – del_lf_rows_len) / btree_space) * 100

Categories: oracle Tags: , , ,

Videos, Slideshows and Podcasts by Cincopa Wordpress Plugin