Skip to content

Data access tuning - 4. page

ITL争用阻塞与ITL死锁

ITL(Interested Transaction List)是Oracle数据块内部的一个组成部分,用来记录该块所有发生的事务,一个itl可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个itl的位置就可以被反复使用了,因为itl类似记录,所以,有的时候也叫itl槽位。

如果一个事务一直没有提交,那么,这个事务将一直占用一个itl槽位,itl里面记录了事务信息,回滚段的入口,事务类型等等。如果这个事务已经提交,那么,itl槽位中还保存的有这个事务提交时候的SCN号。如dump一个块,就可以看到itl信息:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.002.0000158e 0x0080104d.00a1.6e –U- 734 fsc 0x0000.6c9deff0
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000

对于已经提交的事务,itl槽位最好不要马上被覆盖,因为一致性读可能会用到这个信息,一致性读的时候,可能需要从这里获得回滚段的入口,并从回滚段中获得一致性读。

itl的个数,受参数initrans控制,最大的itl个数,受maxtrans控制,在一个块内部,默认分配了2个或3个itl的个数,如果这个块内还有空闲空间,那么Oracle是可以利用这些空闲空间并再分配itl。如果没有了空闲空间,那么,这个块因为不能分配新的itl,所以就可能发生itl等待。

如果在并发量特别大的系统中,最好分配足够的itl个数,其实它并浪费不了太多的空间,或者,设置足够的pctfree,保证itl能扩展,但是pctfree有可能是被行数据给消耗掉的,如update,所以,也有可能导致块内部的空间不够而导致itl等待。

以下是一个ITL等待的例子:
Luda@10gR2>create table test(a int) pctfree 0 initrans 1;
Table created.

我们这里指定pctfree为0,initrans为1,就是为了更观察到itl的真实等待情况,那么,现在,我们给这些块内插入数据,把块填满,让它不能有空间分配。
Luda@10gR2>begin
2 for i in 1..2000 loop
3 insert into test values(i);
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed.
Luda@10gR2>commit;
Commit complete.

我们再检查数据填充的情况:
Luda@10gR2>select f,b,count(*) from (
2 select dbms_rowid.rowid_relative_fno(rowid) f,
3 dbms_rowid.rowid_block_number(rowid) b
4 from test) group by f,b;

F B COUNT(*)
———- ———- ———-
1 29690 734
1 29691 734
1 29692 532

可以发现,这2000条数据分布在3个块内部,其中有2个块添满了,一个块是半满的。我们dump一个满的块,可以看到itl信息:
Luda@10gR2>alter system dump datafile 1 block 29690;

回到os,在udump目录下,检查跟踪文件,可以看到如下的信息
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.002.0000158e 0x0080104d.00a1.6e –U- 734 fsc 0x0000.6c9deff0
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000

发现,采用如上参数创建的表,块内部默认有2个itl槽位,如果这里不指定initrans 1,默认是有3个itl槽位的。

因为只有2个ITL槽位,我们可以用三个会话来模拟等待:

会话1,我们更新这个块内部的第一行:
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;
1 row updated.

会话2,我们更新这个块内部的第2行:
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;
1 row updated.

会话3(SID=153),我们更新这个块内部的第三行,发现被阻塞:
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

可以看到,会话被阻塞

观察这个时候的等待事件,我们可以发现是ITL等待:
Luda@10gR2>select EVENT from v$session_wait where sid=153
EVENT
—————————-
enq: TX – allocate ITL entry

因为该块只有2个itl槽位,而现在发生了3个事务,而且,因为该块被数据添满,根本没有剩余的空间来分配新的itl,所以发生了等待。如果我们这个实验发生在半满的块29692上面,就发现进程3不会被阻塞,因为这里有足够的空间可以分配新的itl。

3、ITL死锁

那么,理解了itl的阻塞,我们也就可以分析itl的死锁了,因为有阻塞,一般就能发生死锁。还是以上的表,因为有2个itl槽位,我们需要拿2个满的数据块,4个进程来模拟itl死锁:

会话1
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;
1 row updated.

会话2
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;
1 row updated.

会话3
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;
1 row updated.

会话4
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;
1 row updated.

以上4个进程把2个不同块的4个itl槽位给消耗光了,现在的情况,就是让他们互相锁住,达成死锁条件,回到会话1,更新块2,注意,以上4个操作,包括以下的操作,更新的根本不是同一行数据,主要是为了防止出现的是TX等待。
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

发现被阻塞

那我们在会话3,更新块1,当然,也不是同一行
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

被阻塞

注意,如果是9i,在这里就报死锁了,在进程1,我们可以看到
Luda@9iR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;
update test set a=a
where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource

但是,在10g里面,这个时候,死锁是不会发生的,因为这里的进程1还可以等待进程4释放资源,进程3还可以等待进程2释放资源,只要进程2与进程4释放了资源,整个环境又活了,那么我们需要把这两个进程也塞住。

会话2,注意,我们也不是更新的同一行数据
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;

被阻塞

还有最后一个进程,进程4,我们也不更新同一行数据
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;

虽然,以上的每个更新语句,更新的都不是同一个数据行,但是,的确,所有的进程都被阻塞住了,那么,死锁的条件也达到了,马上,我们可以看到,进程1出现提示,死锁:
Luda@10gR2>update test set a=a
2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

update test set a=a
where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource

4、ITL等待与死锁的避免

为了避免以上的情况发生,我们一定要注意在高并发环境下的表中,正确的设置itl个数,如4个,8个等等,保证该块有足够的itl槽位,保证事务能顺利的进行,而没有itl的等待。关于itl的等待,在statspack的段报告中,也能很明显的看到:
Top 5 ITL Waits per Segment for DB: TEST Instance: test Snaps: 13013 -13014
-> End Segment ITL Waits Threshold: 100

Subobject Obj. ITL
Owner Tablespace Object Name Name Type Waits %Total
———- ———- ——————– ———- —– ———— ——-
TEST TBS_EL_IND IDX_LLORDER_ORDERID INDEX 3 75.00
TEST TBS_INDEX2 IDX_AUC_FEED_FDATE INDEX 1 25.00

如果出现的频率很小,象上面的情况,一般可以不用干预,但是,如果waits很多,则表示这个对象有很严重的itl争用情况,需要增加itl个数。
如果想增加initrans个数,参数可以动态修改,但是,只是针对以后的新块起效,以前的块如果想生效,需要在新参数下,重整表数据,如重建该表,或者move该表。

ASSM三级位图结构

L1、L2、L3块的作用:–方便查找数据块。

L1中有指向L3的指针,L2有指向L3的指针,L3中有多个数据块的指针和状态。
1、每个L3中,有多个L2的地址(第一个L3是段头)。
2、每个L2中,有多个L1的地址。
3、每个L1中,有多个数据块地址。
Oracle最多支持三级位图。
一级位图用于管理具体数据块的使用。
二级位图块记录了一级位图块的地址。
三级位图块记录了二级位图块的地址。Segment Heade可以管理极大数据量的对象的空间,很难出现另一个三级位图块。

bmp

1.如何查找段头–第一个L3块

BYS@ bys3>create tablespace test2 datafile ‘/u01/oradata/bys3/test22.dbf’ size 10m;
Tablespace created.
BYS@ bys3>create table test2(aa varchar2(10)) tablespace test2;
Table created.
BYS@ bys3>insert into test2 values(789);
1 row created.
BYS@ bys3>commit;
Commit complete.
BYS@ bys3>select dbms_rowid.ROWID_BLOCK_NUMBER(rowid),dbms_rowid.ROWID_RELATIVE_FNO(rowid) ,aa from test2;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) AA
———————————— ———————————— ———-
131 7 789
BYS@ bys3>select header_block,header_file fromdba_segments where segment_name=’TEST2′ and owner=’BYS’;
HEADER_BLOCK HEADER_FILE ——-从这语句找到段头
———— ———–
130 7
BYS@ bys3>select object_id,data_object_id from dba_objects where object_name=’TEST2′ and owner=’BYS’;
OBJECT_ID DATA_OBJECT_ID
———- ————–
23199 23199
BYS@ bys3>alter table test2 allocate extent (size 960k);
Table altered.
BYS@ bys3>alter system dump datafile 7 block 130;
System altered.
BYS@ bys3>select value from v$diag_info where name like ‘De%’;
VALUE
—————————————————————————————————-
/u01/diag/rdbms/bys3/bys3/trace/bys3_ora_4215.trc
################

段头–第一个L3块的解读:

Start dump data blocks tsn: 7 file#:7 minblk 130 maxblk 130
Block dump from cache:
Dump of buffer cache at level 4 for tsn=7 rdba=29360258
BH (0x20fe6d64) file#: 7 rdba: 0x01c00082 (7/130) class: 4 ba: 0x20cb6000
set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,0
dbwrid: 0 obj: 23199 objn: 23199 tsn: 7 afn: 7 hint: f
hash: [0x2a39d804,0x2a39d804] lru: [0x217f7ccc,0x21befb08]
lru-flags: on_auxiliary_list
ckptq: [NULL] fileq: [NULL] objq: [0x21befb20,0x22beab34] objaq: [0x2443a408,0x2
1befb28]
st: XCURRENT md: NULL fpin: ‘ktswh03: ktscts’ tch: 2
flags: block_written_once redo_since_read
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
Block dump from disk: –下面的才是物理数据文件中内容
buffer tsn: 7 rdba: 0x01c00082 (7/130)
scn: 0x0000.0082b051 seq: 0x01 flg: 0x04 tail: 0xb0512301
frmt: 0x02 chkval: 0xf0d6 type: 0x23=PAGETABLE SEGMENT HEADER –数据块类型:段头
Hex dump of block: st=0, typ_found=1
Dump of memory from 0xB6BC0600 to 0xB6BC2600
B6BC0600 0000A223 01C00082 0082B051 04010000 [#…….Q…….]–B6BC0600 0000A223,这里的23,是数据块中的块头的块类型的信息,与type: 0x23=对应。
Repeat 185 times ——-省略了大部分无关输出
B6BC25F0 00000000 00000000 00000000 B0512301 [………….#Q.]
Extent Control Header
—————————————————————–
Extent Header:: spare1: 0 spare2: 0 #extents: 18 #blocks: 384 –有18个区,384个块==16个区-8 个块,2个区-128个块,16*8+2*128=384
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x01c00088 ext#: 0 blk#: 8 ext size: 8
高水位的块DBA–0x01c00088 -136号块,是第二个区的第一个块。插入只会插入高水位以下的块< =,以区为单位推进--也可能是区的一半-据说算法很复杂。查询时也会找到高水位,查询高水位下的块。 并发插入时候,高水位的位置会影响限制并发插入,因为只能插入高水位以下的块,如8KBLOCK时;1M区,128个块,超过128个并发会有热块,从而产生buffer busy waits.; 如果是8M的区,会有1024个块,超过1024并发同样会有热块产生。 #blocks in seg. hdr's freelists: 0 #blocks below: 5 mapblk 0x00000000 offset: 0 Unlocked -------------------------------------------------------- Low HighWater Mark : --低高水位,低高水位之下的数据都已经使用了。低高水位和高水位之间,有的数据块已经使用,有的未使用-未格式化。 格式化是在块头加:OJBECT号,数据字典中:dba_objects.DATA_OBJECT_ID Highwater:: 0x01c00088 ext#: 0 blk#: 8 ext size: 8 #blocks in seg. hdr's freelists: 0 #blocks below: 5 mapblk 0x00000000 offset: 0 Level 1 BMB for High HWM block: 0x01c00080 Level 1 BMB for Low HWM block: 0x01c00080 -------------------------------------------------------- Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0 L2 Array start offset: 0x00001434 First Level 3 BMB: 0x00000000 L2 Hint for inserts: 0x01c00081 ----二级位图块的DBA,换算为:0000 0001 1100 ,前10位文件号是二进制111-十进制7,16进制81--十进制129.插入时会选择这个L2下的L1的块,直到此L2下的所有L1中的块用完,才会用其它L2--在此处更改L1的DBA。 Last Level 1 BMB: 0x01c00181 Last Level II BMB: 0x01c00081 Last Level III BMB: 0x00000000 Map Header:: next 0x00000000 #extents: 18 obj#: 23199 flag: 0x10000000 Inc # 0 Extent Map --区地图,段下有几个区,区的起始地址以及包含的块数 ----------------------------------------------------------------- 0x01c00080 length: 8 0x01c00088 length: 8 0x01c00090 length: 8 0x01c00098 length: 8 0x01c000a0 length: 8 0x01c000a8 length: 8 0x01c000b0 length: 8 0x01c000b8 length: 8 0x01c000c0 length: 8 0x01c000c8 length: 8 0x01c000d0 length: 8 0x01c000d8 length: 8 0x01c000e0 length: 8 0x01c000e8 length: 8 0x01c000f0 length: 8 0x01c000f8 length: 8 0x01c00100 length: 128 0x01c00180 length: 128 Auxillary Map --辅助表,区由哪个L1管理及所管理的区-数据块的起始位置DBA--非Metadata块始地址。。辅助区地址可看出哪几个区内的块共用同一个L1,如下: -------------------------------------------------------- Extent 0 : L1 dba: 0x01c00080 Data dba: 0x01c00083 --131,第一个可用的块的地址 Extent 1 : L1 dba: 0x01c00080 Data dba: 0x01c00088 --136,可以看到这个L1和上一个L1相同,一个L1管理了两个区。 Extent 2 : L1 dba: 0x01c00090 Data dba: 0x01c00091 --145 Extent 3 : L1 dba: 0x01c00090 Data dba: 0x01c00098 Extent 4 : L1 dba: 0x01c000a0 Data dba: 0x01c000a1 Extent 5 : L1 dba: 0x01c000a0 Data dba: 0x01c000a8 Extent 6 : L1 dba: 0x01c000b0 Data dba: 0x01c000b1 Extent 7 : L1 dba: 0x01c000b0 Data dba: 0x01c000b8 Extent 8 : L1 dba: 0x01c000c0 Data dba: 0x01c000c1 Extent 9 : L1 dba: 0x01c000c0 Data dba: 0x01c000c8 Extent 10 : L1 dba: 0x01c000d0 Data dba: 0x01c000d1 Extent 11 : L1 dba: 0x01c000d0 Data dba: 0x01c000d8 Extent 12 : L1 dba: 0x01c000e0 Data dba: 0x01c000e1 Extent 13 : L1 dba: 0x01c000e0 Data dba: 0x01c000e8 Extent 14 : L1 dba: 0x01c000f0 Data dba: 0x01c000f1 Extent 15 : L1 dba: 0x01c000f0 Data dba: 0x01c000f8 Extent 16 : L1 dba: 0x01c00100 Data dba: 0x01c00102 --128个块了,L1一个管理 一个区。在8KB BLOCK,8M的区时,一个L1管理可以1024个块,再大的区就未实验过了。 Extent 17 : L1 dba: 0x01c00180 Data dba: 0x01c00182 -------------------------------------------------------- Second Level Bitmap block DBAs -------------------------------------------------------- DBA 1: 0x01c00081 --二级位图块的DBA,这里只有一个L2,如果有多个L2,都会在这里显示出来的。 End dump data blocks tsn: 7 file#: 7 minblk 130 maxblk 130 #################################################3 DUMP L2块--根据段头找到L2的DBA BYS@ bys3>alter system dump datafile 7 block 129;
System altered.
BYS@ bys3>select value from v$diag_info where name like ‘De%’;
VALUE
—————————————————————————————————-
/u01/diag/rdbms/bys3/bys3/trace/bys3_ora_4982.trc
#######
Start dump data blocks tsn: 7 file#:7 minblk 129 maxblk 129
Block dump from cache:
Dump of buffer cache at level 4 for tsn=7 rdba=29360257
Block dump from disk:
buffer tsn: 7 rdba: 0x01c00081 (7/129)
scn: 0x0000.0082b04b seq: 0x03 flg: 0x04 tail: 0xb04b2103
frmt: 0x02 chkval: 0xcf4e type: 0x21=SECOND LEVEL BITMAP BLOCK –L2
Hex dump of block: st=0, typ_found=1
Dump of memory from 0xB6B43600 to 0xB6B45600
B6B43600 0000A221 01C00081 0082B04B 04030000 [!…….K…….] 0000A221中21与type: 0x21= 对应
Repeat 496 times
B6B455F0 00000000 00000000 00000000 B04B2103 [………….!K.]
Dump of Second Level Bitmap Block
number: 12 nfree: 12 ffree: 0 pdba: 0x01c00082 –父DBA,也就是L3的地址-130号块, number: 12 –L1的总数,nfree: 12–空闲的L1个数,
Inc #: 0 Objd: 23199
opcode:1
xid:
L1 Ranges :–多个L1时,按L1 DBA顺序插入/////???????插入数据时:再根据PID进行HASH,得到一个随机值,根据此值选择L2中的L1–受高水位影响的
——————————————————–
0x01c00080 Free: 5 Inst: 1 —128号块,Free: 5标记L1中可用空间状态
0x01c00090 Free: 5 Inst: 1 –144号块,Free: 0 FULL状态
0x01c000a0 Free: 5 Inst: 1
0x01c000b0 Free: 5 Inst: 1
0x01c000c0 Free: 5 Inst: 1
0x01c000d0 Free: 5 Inst: 1
0x01c000e0 Free: 5 Inst: 1
0x01c000f0 Free: 5 Inst: 1
0x01c00100 Free: 5 Inst: 1
0x01c00101 Free: 5 Inst: 1
0x01c00180 Free: 5 Inst: 1
0x01c00181 Free: 5 Inst: 1

——————————————————–
End dump data blocks tsn: 7 file#: 7 minblk 129 maxblk 129

#####################################

DUMP L3块–根据L2中的 L1 Ranges的找到 第一个L1块的DBA

BYS@ bys3>alter system dump datafile 7 block 128;
System altered.
BYS@ bys3>select value from v$diag_info where name like ‘De%’;
VALUE
—————————————————————————————————-
/u01/diag/rdbms/bys3/bys3/trace/bys3_ora_5162.trc
@@@@@@@@@@@@@@@
Start dump data blocks tsn: 7 file#:7 minblk 128 maxblk 128
Block dump from cache:
Dump of buffer cache at level 4 for tsn=7 rdba=29360256
Block dump from disk:
buffer tsn: 7 rdba: 0x01c00080 (7/128)
scn: 0x0000.0082afe2 seq: 0x01 flg: 0x04 tail: 0xafe22001
frmt: 0x02 chkval: 0x9d2d type: 0x20=FIRST LEVEL BITMAP BLOCK
Hex dump of block: st=0, typ_found=1
Dump of memory from 0xB6CBD600 to 0xB6CBF600
B6CBD600 0000A220 01C00080 0082AFE2 04010000 [ ……………] 0000A220的20,表示:type: 0x20=FIRST LEVEL BITMAP BLOCK
B6CBD610 00009D2D 00000000 00000000 00000000 [-……………]
B6CBD620 00000000 00000000 00000000 00000000 […………….]
Repeat 1 times
Repeat 8 times
B6CBD780 00000000 00000000 00000000 55551511 […………..UU]
B6CBD790 00000000 00000000 00000000 00000000 […………….]
Repeat 485 times
B6CBF5F0 00000000 00000000 00000000 AFE22001 […………. ..]
Dump of First Level Bitmap Block
——————————–
nbits : 4 nranges: 2 parent dba: 0x01c00081 poffset: 0 –parent dba: 上一级DBA,L2的地址——129号块
unformatted: 8 total: 16 first useful block: 3
owning instance : 1
instance ownership changed at 01/22/2014 11:22:40
Last successful Search 01/22/2014 11:22:40
Freeness Status: nf1 0 nf2 0 nf3 0 nf4 5

Extent Map Block Offset: 4294967295
First free datablock : 3
Bitmap block lock opcode 0
Locker xid: : 0x0000.000.00000000
Dealloc scn: 3.0
Flag: 0x00000001 (-/-/-/-/-/HWM)
Inc #: 0 Objd: 23199
HWM Flag: HWM Set
Highwater:: 0x01c00088 ext#: 0 blk#: 8 ext size: 8 高水位在 0x01c00088 136号块,下一个区的第一个块
#blocks in seg. hdr’s freelists: 0
#blocks below: 5
mapblk 0x00000000 offset: 0
——————————————————–
DBA Ranges : –这个L1管理的数据块的范围
——————————————————–
0x01c00080 Length: 8 Offset: 0
0x01c00088 Length: 8 Offset: 8

0:Metadata 1:Metadata 2:Metadata 3:75-100% free —Metadata 是无数据,这里存放的是L3 L2 L1的块。
4:75-100% free 5:75-100% free 6:75-100% free 7:75-100% free
8:unformatted 9:unformatted 10:unformatted 11:unformatted
12:unformatted 13:unformatted 14:unformatted 15:unformatted 可以DUMP 12和5,对比格式化与未格式化的差别。
在L1块中关于数据块的状态有7种格式:Unformat 75-100% 50-75% 25-50% 0-25% FULL Metadata
——————————————————–
End dump data blocks tsn: 7 file#: 7 minblk 128 maxblk 128

转自互联网仅作记载。

统计信息误差导致执行计划错误:一例

日前在客户现场对一些sp进程程序调优,中间碰到不少问题

列举其一:

 

select item_code from inv_master where  NVL (COST_FLAG,'N') = 'Y';
 
其中在表inv_master的quota_hours字段创建函数索引
create index idx_inv_master_02 on inv_master(nvl(COST_FLAG,'N') online tablespace idxdata;
 
select count(*) from inv_master;
 
————
636687
 
select count(*) from inv_master where  NVL (COST_FLAG,'N') = 'Y';
 
————
123
 
根据单表选择率这里语句

select item_code from inv_master where  NVL (QUOTA_HOURS,'N') = 'Y';

应该会走indx_inv_master_02这个索引按照 range scan方式扫描,但是在客户的库中还是会全表扫描,由于这个sql在sp中要被反复执行3万多次,这个是一个非常严重的IO问题,

在现场首先做了一次

analyze table INV_MASTER compute statistics;

analyze index IDX_INV_MASTER_02 compute statistics; 
sql还是不走索引,没办法只好做一次10053
——————————————————————————————————–
oradebug setmypid
oradebug unlimit
oradebug event 10053 trace name context forever,level 10
select item_code from inv_master where  NVL (QUOTA_HOURS,'N') = 'Y';
oradebug event 10053 trace name context off;
oradebug tracefile_name
 
TRC文件底部显示:
Table Stats::
  Table: INV_MASTER  Alias: INV_MASTER
    #Rows: 63487  #Blks:  3268  AvgRowLen:  364.00
Index Stats::
  Index: IDX_INV_MASTER_BFLAG  Col#: 68
    LVLS: 1  #LB: 70  #DK: 2  LB/K: 35.00  DB/K: 1451.00  CLUF: 2903.00
  Index: IDX_INV_MASTER_COOP_FLAG  Col#: 169
    LVLS: 1  #LB: 116  #DK: 2  LB/K: 58.00  DB/K: 2229.00  CLUF: 4458.00
  Index: IDX_INV_MASTER_COST_FLAG  Col#: 167
    LVLS: 1  #LB: 116  #DK: 2  LB/K: 58.00  DB/K: 1618.00  CLUF: 3236.00
  Index: IDX_INV_MASTER_FLAG_FC  Col#: 168 167
    LVLS: 1  #LB: 133  #DK: 4  LB/K: 33.00  DB/K: 1098.00  CLUF: 4395.00
  Index: IDX_INV_MASTER_FLAG_PHFL  Col#: 168
    LVLS: 1  #LB: 116  #DK: 3  LB/K: 38.00  DB/K: 1461.00  CLUF: 4384.00
  Index: PK_INV_MASTER  Col#: 3
    LVLS: 1  #LB: 236  #DK: 63487  LB/K: 1.00  DB/K: 1.00  CLUF: 28932.00
***************************************
SINGLE TABLE ACCESS PATH
  —————————————–
  BEGIN Single Table Cardinality Estimation
  —————————————–
  Column (#167): SYS_NC00167$(NVARCHAR2)
    AvgLen: 1.00 NDV: 2 Nulls: 0 Density: 0.5
  Column (#56): COST_FLAG(NVARCHAR2)
    AvgLen: 1.00 NDV: 2 Nulls: 16 Density: 0.5
  Table: INV_MASTER  Alias: INV_MASTER
    Card: Original: 63487  Rounded: 31751  Computed: 31751.50  Non Adjusted: 31751.50
  —————————————–
  END   Single Table Cardinality Estimation
  —————————————–
  Access Path: TableScan
    Cost:  725.29  Resp: 725.29  Degree: 0
      Cost_io: 717.00  Cost_cpu: 107074906
      Resp_io: 717.00  Resp_cpu: 107074906
kkofmx: index filter:NVL("INV_MASTER"."COST_FLAG",U'N')=U'N'
  Access Path: index (AllEqRange)
    Index: IDX_INV_MASTER_COST_FLAG
    resc_io: 1678.00  resc_cpu: 57991026
    ix_sel: 0.50013  ix_sel_with_filters: 0.50013
    Cost: 1682.49  Resp: 1682.49  Degree: 1
  Access Path: index (FullScan)
    Index: IDX_INV_MASTER_FLAG_FC
    resc_io: 2333.00  resc_cpu: 69001720
    ix_sel: 1  ix_sel_with_filters: 0.50013
    Cost: 2338.69  Resp: 2338.69  Degree: 1
 
CBO在选择执行计划时候是根据统计信息来选择的,在这里可以发现统计信息中走这个索引的消耗为1682.49,走全表扫才717的消耗,
可以看出这里的索引统计信息肯定有问题,可以从dba_segements可以计算出索引idx_inv_master_cost_flag总的块为10多个,和这里明显不符,重新收集统计信息后,全表扫改为INDEX RANGE SCAN
 
 
 
over.
exec dbms_stats.gather_table_stats('RS10','INV_MASTER');

Oracle收集优化器统计信息

优化器统计范围:

表统计; –行数,块数,行平均长度;all_tables:NUM_ROWS,BLOCKS,AVG_ROW_LEN;
列统计; –列中唯一值的数量(NDV),NULL值的数量,数据分布;
–DBA_TAB_COLUMNS:NUM_DISTINCT,NUM_NULLS,HISTOGRAM;
索引统计;–叶块数量,等级,聚簇因子;
–DBA_INDEXES:LEAF_BLOCKS,CLUSTERING_FACTOR,BLEVEL;
系统统计;–I/O性能与使用率;
–CPU性能与使用率;
–存储在aux_stats$中,需要使用dbms_stats收集,I/O统计在X$KCFIO中;

————-
analyze
————-
需要使用ANALYZE统计的统计:
使用LIST CHAINED ROWS和VALIDATE子句;
收集空闲列表块的统计;
Analyze table tablename compute statistics;
Analyze index|cluster indexname estimate statistics;
ANALYZE TABLE tablename COMPUTE STATISTICS
FOR TABLE
FOR ALL [LOCAL] INDEXES
FOR ALL [INDEXED] COLUMNS;
ANALYZE TABLE tablename DELETE STATISTICS
ANALYZE TABLE tablename VALIDATE REF UPDATE
ANALYZE TABLE tablename VALIDATE STRUCTURE [CASCADE]|[INTO TableName]
ANALYZE TABLE tablename LIST CHAINED ROWS [INTO TableName]
ANALYZE 不适合做分区表的分析
———————-
dbms_stats
———————-
dbms_stats能良好地估计统计数据(尤其是针对较大的分区表),并能获得更好的统计结果,最终制定出速度更快的SQL执行计划。
这个包的下面四个存储过程分别收集index、table、schema、database的统计信息:
dbms_stats.gather_table_stats 收集表、列和索引的统计信息;
dbms_stats.gather_schema_stats 收集SCHEMA下所有对象的统计信息;
dbms_stats.gather_index_stats 收集索引的统计信息;
dbms_stats.gather_system_stats 收集系统统计信息
dbms_stats.GATHER_DICTIONARY_STATS: 所有字典对象的统计;
DBMS_STATS.GATHER_DICTIONARY_STATS 其收集所有系统模式的统计

dbms_stats.delete_table_stats 删除表的统计信息
dbms_stats.delete_index_stats 删除索引的统计信息
dbms_stats.export_table_stats 输出表的统计信息
dbms_stats.create_state_table
dbms_stats.set_table_stats 设置表的统计
dbms_stats.auto_sample_size

统计收集的权限
==========================
必须授予普通用户权限
sys@ORADB> grant execute_catalog_role to hr;
sys@ORADB> grant connect,resource,analyze any to hr;

统计收集的时间考虑
==========================
当参数STATISTICS_LEVEL设置为TYPICAL或者ALL,系统会在夜间自动收集统计信息。
查看系统自动收集统计信息的job:
SELECT * FROM dba_scheduler_jobs WHERE job_name = ‘GATHER_STATS_JOB’;
也可以disable自动收集统计信息:
BEGIN
dbms_scheduler.disable(‘GATHER_STATS_JOB’);
END;

使用手工统计

对所有更改活动中等的对象自动统计应该足够充分,由于自动统计收集在夜间进行,因此对于一些更新频繁的对象其统计可能已经过期。两种典型的对象:
高度变化的表在白天的活动期间被TRUNCATE/DROP并重建;
块加载超过本身总大小10%的对象;

对于第一种对象可以使用以下两种方法:
1 将这些表上的统计设置为NULL,当Oracle遇到没有统计的表时,将动态收集必要的统计作为查询优化的一部分;
动态收集特征由OPTIMIZER_DYNAMIC_SAMPLING控制,这个参数应该设置为大于等于2,默认为2。可以通过删除并锁住统计将统计设置为NULL:
DBMS_STATS.DELETE_TABLE_STATS(‘SCHEMA’,’TABLE’);
DBMS_STATS.LOCK_TABLE_STATS(‘SCHEMA’,’TABLE’);

2 将这些表上的统计设置为代表表典型状态的值。在表具有某个有代表性的值时收集统计,然后锁住统计;
由于夜间收集的统计未必适合于白天的负载,因此这些情况下使用手工收集比GATHER_STATS_JOB更有效。
对于块加载,统计应该在加载后立刻收集,通常合并在加载语句的后面防止遗忘。
对于外部表,统计不能通过GATHER_DATABASE_STATS,GATHER_SCHEMA_STATS以及自动统计收集收集。因此需要使用GATHER_TABLE_STATS在单个表上收集统计,并且在外部表上不支持取样,ESTIMATE_PERCENT应该被显示设置为NULL。
如果STATISTICS_LEVEL设置为BASIC禁用了监控特征,自动统计收集将不会检测过期的统计,此时需要手工收集。

3 需要手工收集的另一个地方是系统统计,其不会自动收集。
对于固定表,如动态性能表,需要使用GATHER_FIXED_OBJECTS_STATS收集,这些表上的统计应该在数据库具有有代表性的活动后收集。

统计收集考虑
==========================
1 统计收集使用取样

不使用抽样的统计收集需要全表扫描并且排序整个表,抽样最小化收集统计的必要资源。
Oracle推荐设置DBMS_STATS的ESTIMATE_PERCENT参数为DBMS_STATS.AUTO_SAMPLE_SIZE在达到必要的统计精确性的同时最大化性能。

2 并行统计收集
Oracle推荐设置DBMS_STATS的DEGREE参数为DBMS_STATS.AUTO_DEGREE,该参数允许Oracle根据对象的大小和并行性初始化参数的设置选择恰当的并行度。
聚簇索引,域索引,位图连接索引不能并行收集。

3 分区对象的统计收集
对于分区表和索引,DBMS_STATS可以收集单独分区的统计和全局分区,对于组合分区,可以收集子分区,分区,表/索引上的统计,分区统计的收集可以通过声明参数GRANULARITY。根据将优化的SQL语句,优化器可以选择使用分区统计或全局统计,对于大多数系统这两种统计都是很重要的,Oracle推荐将GRANULARITY设置为AUTO同时收集全部信息。

4 列统计和直方图
当在表上收集统计时,DBMS_STATS收集表中列的数据分布的信息,数据分布最基本的信息是最大值和最小值,但是如果数据分布是倾斜的,这种级别的统计对于优化器来说不够的,对于倾斜的数据分布,直方图通常用来作为列统计的一部分。
直方图通过METHOD_OPT参数声明,Oracle推荐设置METHOD_OPT为FOR ALL COLUMNS SIZE AUTO,使用该值时Oracle自动决定需要直方图的列以及每个直方图的桶数。也可以手工设置需要直方图的列以及桶数。
如果在使用DBMS_STATS的时候需要删除表中的所有行,需要使用TRUNCATE代替drop/create,否则自动统计收集特征使用的负载信息以及RESTORE_*_STATS使用的保存的统计历史将丢失。这些特征将无法正常发挥作用。

5 确定过期的统计
对于那些随着时间更改的对象必须周期性收集统计,为了确定过期的统计,Oracle提供了一个表监控这些更改,这些监控默认情况下在STATISTICS_LEVEL为TYPICAL/ALL时启用,该表为USER_TAB_MODIFICATIONS。使用DBMS_STATS.FLUSH_DATABASE _MONITORING_INFO可以立刻反映内存中超过监控的信息。在OPTIONS参数设置为GATHER STALE or GATHER AUTO时,DBMS_STATS收集过期统计的对象的统计。

6 用户定义统计
在创建了基于索引的统计后,应该在表上收集新的列统计,这可以通过调用过程设置METHOD_OPT的FOR ALL HIDDEN COLUMNS。

7 何时收集统计
对于增量更改的表,可能每个月/每周只需要收集一次,而对于加载后表,通常在加载脚本中增加收集统计的脚本。对于分区表,如果仅仅是一个分区有了较大改动,只需要收集一个分区的统计,但是收集整个表的分区也是必要的。

系统统计
==========================
系统统计描述系统硬件的特征,包括I/O和CPU。在选择执行计划时,优化器考虑查询所需的CPU和I/O代价。系统统计允许优化器更加精确的评价CPU和IO代价,选择更好的查询计划。
使用DBMS_STATS.GATHER_SYSTEM_STATS收集系统统计,Oracle推荐收集系统统计。收集系统统计需要DBA权限。
收集的优化器系统统计包括:
cpuspeedNW:代表无负载CPU速度,CPU速度为每秒钟CPU周期数;通过设置gathering_mode = NOWORKLOAD或手工设置统计;单位Millions/sec。
ioseektim:I/O查找时间=查找时间+延迟时间+OS负载时间;通过设置gathering_mode = NOWORKLOAD或手工设置统计;单位为ms。
Iotfrspeed:I/O传输速度;通过设置gathering_mode = NOWORKLOAD或手工设置统计;单位为Bytes/ms.
Cpuspeed:代表有负载CPU速度,CPU速度为每秒钟CPU周期数;通过设置gathering_mode =NOWORKLOAD,INTERVAL, START|STOP或手工设置统计;单位Millions/sec。
Maxthr:最大I/O吞吐量;通过设置gathering_mode =NOWORKLOAD,INTERVAL, START|STOP或手工设置统计;单位Bytes/sec.
Slavethr:服务I/O吞吐量是平均并行服务I/O吞吐量;通过设置gathering_mode = INTERVAL,START|STOP或手工设置统计;Bytes/sec.
Sreadtim:随机读取单块的平均时间;通过设置gathering_mode =INTERVAL,START|STOP或手工设置统计;单位为ms。
Mreadtim:顺序读取多块的平均时间,通过设置通过设置gathering_mode = INTERVAL,START|STOP或手工设置统计;单位为ms。
Mbrc: 多块读平均每次读取的块数量;通过设置通过设置gathering_mode = INTERVAL,START|STOP或手工设置统计;单位为blocks。

系统统计的重新收集不会导致当前的SQL无效,只是所有的新SQL语句使用新的统计。

Oracle提供两个选项收集统计:负载统计;非负载统计。

负载统计
==========================
在负载窗口的开始运行dbms_stats.gather_system_stats(’start’),然后运行dbms_stats.gather_system_stats(’stop’)结束负载窗口。
运行dbms_stats.gather_system_stats(’interval’, interval=>N),N表示N分钟后系统统计收集结束。
运行dbms_stats.delete_system_stats()删除负载统计。

非负载统计
==========================
运行不带参数的dbms_stats.gather_system_stats()收集非负载统计,运行非负载统计时会有一定的I/O负载。在某些情况下,非负载统计的值可能会保持默认,此时需要使用dbms_stats.set_system_stats设置。

管理统计
==========================
转储先前版本的统计
使用RESTORE过程转储先前版本的统计,这些过程使用一个时间戳作为参数,包含统计时间的视图包括:
1 DBA_OPTSTAT_OPERATIONS:其中包含了使用DBMS_STATS在模式/系统级别执行的统计操作;
2 *_TAB_STATS_HISTORY:包含了表统计更改的历史。
旧的统计定期刷新,根据DBMS_STATS的ALTER_STATS_HISTORY_RETENTION过程设置而定,默认为31天。
默认情况下,如果STATISTICS_LEVEL为TYPICAL/ALL,自动刷新启用;否则需要使用PURGE_STAT手工刷新。

其他转储与刷新相关的信息包括:
PURGE_STATS: 手工刷新超过某个时间戳的旧统计;
GET_STATS_HISTORY_RENTENTION: 得到当前历史统计保留值;
GET_STATS_HISTORY_AVAILABILTY: 得到可用的最旧的统计的时间戳。
转储的限制:
1 不能转储用户定义统计;
2 如果使用了ANALYZE收集,旧的统计将无法转储。

导入/导出统计
==========================
导出统计前需要使用DBMS_STATS.CREATE_STAT_TABLE创建一个统计表保留统计,在表创建后可以使用DBMS_STATS.EXPORT_*_STATS导出统计到自定义表,这些统计可以使用DBMS_STATS.IMPORT_*_STATS重新导入。
也可以使用IMP/EXP导到其他数据库。

转储统计与导入导出统计

使用转储的情况:
1 恢复旧版本的统计;
2 希望数据库管理统计历史的保留和刷新;
使用EXPORT/IMPORT_*_STATS的情况:
1 实验各种值的不同情况;
2 移动统计到不同数据库;
3 保留统计数据更长的时间。

锁住表和模式的统计
==========================
一旦统计被锁住,将无法在更改这些统计直到被解锁。DBMS_STAT提供两个过程用于解锁,两个用于加锁:
1 LOCK_SCHEMA_STATS;¡¤LOCK_TABLE_STATS;
2 UNLOCK_SCHEMA_STATS;¡¤UNLOCK_TABLE_STATS;

设置统计
==========================
可以使用SET_*_STATISTICS设置表,索引,列,系统统计。

使用动态取样评价统计
==========================
动态取样的目的是通过为谓词选择性和表/索引统计确定更加精确的估计提高服务器性能,估计越精确产生的性能更好。
可以使用动态取样的情况:
1 在收集的统计不能使用或会导致严重的估计错误时估计单表的谓词选择性;
2 估计没有统计的表/索引的统计;
3 估计统计过期的表和索引的统计;
动态取样特征由参数OPTIMIZER_DYNAMIC_SAMPLING控制,默认级别为2。

动态取样的工作机制
主要的性能特征是编译时,Oracle在编译时决定一个查询是否能通过取样获益,如果可以,将用递归SQL随机扫描一小部分表块,然后应用相关的单表谓词评价谓词选择性。

使用动态取样的时间
使用动态取样将获益的情况:
1 可以发现更好的执行计划;
2 取样时间仅占总时间的一小部分;
3 查询将执行多次;

取样级别
==========================
范围从1..10

缺失统计处理
==========================
当Oracle遇到丢失统计时,优化器动态必要的统计。在某些情况下,Oracle无法执行动态取样,包括:远程表/外部表,此时将使用默认统计。
缺失统计时的表默认值:
1 Cardinality:num_of_blocks * (block_size – cache_layer) / avg_row_len
2 Average row length:100字节;
3 Number of blocks:100或基于分区映射的实际值;
4 Remote cardinality:2000行;
5 Remote average row length:100字节;
缺失统计时的索引默认值:
Levels:1
Leaf blocks:25
Leaf blocks/key:1
Data blocks/key:1
Distinct keys:100
Clustering factor:800

gather_schema_stats
==========================
begin
dbms_stats.gather_schema_stats( wnname => ‘SCOTT’,
ptions => ‘GATHER AUTO’,
estimate_percent => dbms_stats.auto_sample_size,
method_opt => ‘for all columns size repeat’,
degree => 15 );
end;
options参数使用4个预设的方法:
gather——重新分析整个架构(Schema)。
gather empty——只分析目前还没有统计的表。
gather stale——只重新分析修改量超过10%的表(这些修改包括插入、更新和删除)。
gather auto——重新分析当前没有统计的对象,以及统计数据过期(变脏)的对象。类似于组合使用gather stale和gather empty。

注意,无论gather stale还是gather auto,都要求进行监视。
如果你执行一个alter table xxx monitoring命令,Oracle会用dba_tab_modifications视图来跟踪发生变动的表。
这样一来,你就确切地知道,自从上一次分析统计数据以来,发生了多少次插入、更新和删除操作。
SELECT * FROM Sys.Dba_Tab_Modifications WHERE Table_Owner = ‘SCOTT’;
使用alter table xxx monitoring命令来实现Oracle表监视时,需要使用dbms_stats中的auto选项。
auto选项根据数据分布以及应用程序访问列的方式(例如通过监视而确定的一个列的工作量)
来创建直方图。使用method_opt=>’auto’类似于在dbms_stats的option参数中使用gather auto。
begin
dbms_stats.gather_schema_stats(ownname => ‘SCOTT’,
estimate_percent => dbms_stats.auto_sample_size,
method_opt => ‘for all columns size auto’,
degree => 7);
end;

estimate_percent选项
以下estimate_percent参数是一种比较新的设计,它允许Oracle的dbms_stats在收集统计数据时,自动估计要采样的一个segment的最佳百分比:
estimate_percent => dbms_stats.auto_sample_size
要验证自动统计采样的准确性,你可检视dba_tables sample_size列。一个有趣的地方是,在使用自动采样时,Oracle会为一个样本尺寸选择5到20的百分比。记住,统计数据质量越好,CBO做出的决定越好。

method_opt选项
dbms_stats的method_opt参数尤其适合在表和索引数据发生变化时刷新统计数据。method_opt参数也适合用于判断哪些列需要直方图(histograms)。
某些情况下,索引内的各个值的分布会影响CBO是使用一个索引还是执行一次全表扫描的决策。例如,假如在where子句中指定的值的数量不对称,全表扫描就显得比索引访问更经济。
如果你有一个高度倾斜的索引(某些值的行数不对称),就可创建Oracle直方图统计。但在现实世界中,出现这种情况的机率相当小。使用CBO时,最常见的错误之一就是在CBO统计中不必要地引入直方图。根据经验,只有在列值要求必须修改执行计划时,才应使用直方图。
为了智能地生成直方图,Oracle为dbms_stats准备了method_opt参数。在method_opt子句中,还有一些重要的新选项,包括skewonly,repeat和auto:method_opt=>’for all columns size skewonly’
method_opt=>’for all columns size repeat’
method_opt=>’for all columns size auto’

skewonly选项会耗费大量处理时间,因为它要检查每个索引中的每个列的值的分布情况。
假如dbms_stat发现一个索引的各个列分布得不均匀,就会为那个索引创建直方图,帮助基于代价的SQL优化器决定是进行索引访问,还是进行全表扫描访问。例如,在一个索引中,假定有一个列在50%的行中,那么为了检索这些行,全表扫描的速度会快于索引扫描。
–*************************************************************
— SKEWONLY option—Detailed analysis

— Use this method for a first-time analysis for skewed indexes
— This runs a long time because all indexes are examined
–*************************************************************
begin
dbms_stats.gather_schema_stats(ownname => ‘SCOTT’,
estimate_percent => dbms_stats.auto_sample_size,
method_opt => ‘for all columns size skewonly’,
degree => 7);
end;

重新分析统计数据时,使用repeat选项,重新分析任务所消耗的资源就会少一些。使用repeat选项时,只会为现有的直方图重新分析索引,不再搜索其他直方图机会。定期重新分析统计数据时,你应该采取这种方式。
–**************************************************************
— REPEAT OPTION – Only reanalyze histograms for indexes
— that have histograms

— Following the initial analysis, the weekly analysis
— job will use the “repeat” option. The repeat option
— tells dbms_stats that no indexes have changed, and
— it will only reanalyze histograms for
— indexes that have histograms.
–**************************************************************
begin
dbms_stats.gather_schema_stats(ownname => ‘SCOTT’,
estimate_percent => dbms_stats.auto_sample_size,
method_opt => ‘for all columns size repeat’,
degree => 7);
end;

Oracle中关于表的统计信息是在数据字典中的,可以下SQL查询到:
SELECT Table_Name,Num_Rows,Blocks,Empty_Blocks,Avg_Space,Chain_Cnt,Avg_Row_Len,Sample_Size,Last_Analyzed
FROM Dba_Tables WHERE wner = ‘SCOTT’ ;

这是对命令与工具包的一些总结
1、对于分区表,建议使用DBMS_STATS,而不是使用Analyze语句。
a) 可以并行进行,对多个用户,多个Table
b) 可以得到整个分区表的数据和单个分区的数据。
c) 可以在不同级别上Compute Statistics:单个分区,子分区,全表,所有分区 ,但不收集聚簇统计
d) 可以倒出统计信息
e) 可以用户自动收集统计信息
2、DBMS_STATS的缺点
a) 不能Validate Structure
b) 不能收集CHAINED ROWS, 不能收集CLUSTER TABLE的信息,这两个仍旧需要使用Analyze语句。
c) DBMS_STATS 默认不对索引进行Analyze,因为默认Cascade是False,需要手工指定为True
3、对于External Table,Analyze不能使用,只能使用DBMS_STATS来收集信息。

GATHER_TABLE_STATS
==========================
DBMS_STATS.gather_table_stats
(ownname varchar2,
tabname varchar2,
partname varchar2 default null,
estimate_percent number default to_estimate_percent_type(get_param(‘ESTIMATE_PERCENT’)),
block_sample boolean default FALSE,
method_opt varchar2 default get_param(‘METHOD_OPT’),
degree number default to_degree_type(get_param(‘DEGREE’)),
granularity varchar2 default get_param(‘GRANULARITY’),
cascade boolean default to_cascade_type(get_param(‘CASCADE’)),
stattab varchar2 default null, statid varchar2 default null,
statown varchar2 default null,
no_invalidate boolean default to_no_invalidate_type(get_param(‘NO_INVALIDATE’)),
stattype varchar2 default ‘DATA’,
force boolean default FALSE);

参数说明:
ownname: 要分析表的拥有者
tabname: 要分析的表名.
partname: 分区的名字,只对分区表或分区索引有用.
estimate_percent:采样行的百分比,取值范围[0.000001,100],null为全部分析,不采样. 常量:DBMS_STATS.AUTO_SAMPLE_SIZE是默认值,由oracle决定最佳取采样值.
block_sapmple:是否用块采样代替行采样.
method_opt: 决定histograms信息是怎样被统计的.method_opt的取值如下:
for all columns:统计所有列的histograms.
for all indexed columns:统计所有indexed列的histograms.
for all hidden columns:统计你看不到列的histograms
for columns SIZE | REPEAT | AUTO | SKEWONLY:
统计指定列的histograms.N的取值范围[1,254]; R
EPEAT上次统计过的histograms;
AUTO由oracle决定N的大小;
SKEWONLY multiple end-points with the same value which is what we define by “there is skew in the data
degree: 设置收集统计信息的并行度.默认值为null.
granularity:Granularity of statistics to collect ,only pertinent if the table is partitioned.
cascade: 是收集索引的信息.默认为falase.
stattab 指定要存储统计信息的表,statid如果多个表的统计信息存储在同一个stattab中用于进行区分.statown存储统计信息表的拥有者.以上三个参数若不指定,统计信息会直接更新到数据字典.
no_invalidate: Does not invalidate the dependent cursors if set to TRUE. The procedure invalidates the dependent cursors immediately if set to FALSE.
force: 即使表锁住了也收集统计信息

例子:
execute dbms_stats.gather_table_stats(ownname => ‘owner’,
tabname => ‘table_name’ ,
estimate_percent => null ,
method_opt => ‘for all indexed columns’ ,
cascade => true);
GATHER_INDEX_STATS
==========================
BEGIN
SYS.DBMS_STATS.GATHER_INDEX_STATS (OwnName => ‘ABC’,
IndName => ‘IDX_FUNC_ABC’,
Estimate_Percent => 10,
Degree => SYS.DBMS_STATS.DEFAULT_DEGREE,
No_Invalidate => FALSE);
END;

—————————————
10g自动收集统计信息
—————————————
从10g开始,Oracle在建库后就默认创建了一个名为GATHER_STATS_JOB的定时任务,用于自动收集CBO的统计信息。
这个自动任务默认情况下在工作日晚上10:00-6:00和周末全天开启。
调用DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC收集统计信息。该过程首先检测统计信息缺失和陈旧的对象。然后确定优先级,再开始进行统计信息。

可以通过以下查询这个JOB的运行情况:
SELECT * FROM Dba_Scheduler_Jobs WHERE Job_Name = ‘GATHER_STATS_JOB’;
其实同在10点运行的Job还有一个AUTO_SPACE_ADVISOR_JOB:
SELECT Job_Name, Last_Start_Date FROM Dba_Scheduler_Jobs;

JOB_NAME LAST_START_DATE
—————————— ————————————
AUTO_SPACE_ADVISOR_JOB 30-OCT-08 10.00.01.463000 PM +08:00
GATHER_STATS_JOB 30-OCT-08 10.00.01.463000 PM +08:00

然而这个自动化功能已经影响了很多系统的正常运行,晚上10点对于大部分生产系统也并非空闲时段。
而自动分析可能导致极为严重的闩锁竞争,进而可能导致数据库Hang或者Crash。
所以建议最好关闭这个自动统计信息收集功能:
关闭及开启自动搜集功能,有两种方法,分别如下:
方法一:
exec dbms_scheduler.disable(‘SYS.GATHER_STATS_JOB’);
exec dbms_scheduler.enable(‘SYS.GATHER_STATS_JOB’);
方法二:
alter system set “_optimizer_autostats_job”=false scope=spfile;
alter system set “_optimizer_autostats_job”=true scope=spfile;

—————————————
查看统计
—————————————
表/索引/列上的统计
DBA_TABLES
DBA_OBJECT_TABLES
DBA_TAB_STATISTICS
DBA_TAB_COL_STATISTICS
DBA_TAB_HISTOGRAMS
DBA_INDEXES
DBA_IND_STATISTICS
DBA_CLUSTERS
DBA_TAB_PARTITIONS
DBA_TAB_SUBPARTITIONS
DBA_IND_PARTITIONS
DBA_IND_SUBPARTITIONS
DBA_PART_COL_STATISTICS
DBA_PART_HISTOGRAMS
DBA_SUBPART_COL_STATISTICS
DBA_SUBPART_HISTOGRAMS
—————————————
直方图统计
—————————————
直方图的类型存储在*TAB_COL_STATISTICS视图的HISTOGRAM列上。

——————————————————————————
bde_last_analyzed.sql – Verifies CBO Statistics
——————————————————————————
bde_last_analyzed.sql verifies the CBO statistics in the data dictionary for all tables, indexes, and partitions. It also validates the statistics on tables and indexes owned by ‘SYS’.

The 5 generated reports bde_last_analyzed_xxx.html, present the total of tables and indexes analyzed per module and per date.

Script. bde_last_analyzed.sql provided in this Note can be used on any 8i, 9i, 10g, 11g or higher database, including Oracle Apps 11i and R12 instances

如果是ERP数据库,则用APPS连接,否则用其他任何SYS权限用户连接都可以
#sqlplus / SQL> START bde_last_analyzed.sql

Review spool output files bde_last_analyzed_xxx.html files. Spool files get created on same directory from which this script. is executed. On NT, files may get created under $ORACLE_HOME/bin.

If some modules have not been analyzed, or they have but not recently, these Apps objects must be analyzed using FND_STATS or coe_stats.sql if belonging to Oracle Apps. Otherwise use DBMS_STATS.
If Oracle Apps, use corresponding concurrent program with an estimate of 10%, or execute equivalent FND_STATS procedure from SQL*Plus:
SQL> exec fnd_stats.gather_schema_statistics(‘APPLSYS’); Where ‘APPLSYS’ is the module (schema) that requires new statistics.

If only a few tables require to have their statistics gathered, use the corresponding concurrent program to gather stats by table, or execute equivalent FND_STATS procedure from SQL*Plus:
SQL> exec fnd_stats.gather_table_stats(‘MRP’,’MRP_FORECAST_DATES’);
Where ‘MRP’ is the schema owner, and ‘MRP_FORECAST_DATES’ is the table name. This syntax is only for non-partitioned Tables.

If any Partitioned Table requires its Global Stats being rebuilt, it is because at some point you gathered Stats on the table using a granularity of PARTITION. See second method below:
begin
dbms_stats.delete_table_stats(ownname => ‘APPLSYS’, tabname => ‘WF_ITEM_ACTIVITY_STATUSES’);
fnd_stats.gather_table_stats (ownname => ‘APPLSYS’, tabname => ‘WF_ITEM_ACTIVITY_STATUSES’,
granularity => ‘DEFAULT’);
end;
/

Once you fix your stats, be sure to ALWAYS use the granularity of DEFAULT for partitioned tables.

If you want to execute this bde_last_analyzed.sql script. against only one schema, modify DEF SCHEMA code line.

—————————————
分区表的统计信息实例
—————————————
ORATEA ORACLE的统计信息在执行SQL的过程中扮演着非常重要的作用,而且ORACLE在表的各个层次都会有不同的统计信息,通过这些统计信息来描述表的,列的各种各样的统计信息。下面通过一个复合分区表来说明一些常见的和常见的统计信息。

SQL>
create table test
partition by range(object_id)
subpartition by hash(object_type) subpartitions 4
(partition p1 values less than(10000),
partition p2 values less than(20000),
partition p3 values less than(30000),
partition p4 values less than(maxvalue))
as
select * from dba_objects;

表已创建。
sql>
BEGIN
dbms_stats.gather_table_stats(ownname => ‘SCOTT’,
tabname => ‘TEST’,
estimate_percent => 100,
block_sample => FALSE,
method_opt => ‘FOR ALL COLUMNS SIZE 10’,
granularity => ‘ALL’,
cascade => TRUE);
END;

1,表级的统计信息

SQL> select table_name,num_rows,blocks,empty_blocks,avg_space from user_tables where table_name = ‘TEST’;

TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
—————————— ———- ———- ———— ———-
TEST 50705 788 0 0

2,表上列的统计信息

SQL> select table_name,column_name,num_distinct,density from user_tab_columns where table_name = ‘TEST’;

TABLE_NAME COLUMN_NAME NUM_DISTINCT DENSITY
—————————— —————————— ———— ———-
TEST OWNER 25 .365014295
TEST OBJECT_NAME 30275 .000039205
TEST SUBOBJECT_NAME 191 .015657993
TEST OBJECT_ID 50705 .000019722
TEST DATA_OBJECT_ID 4334 .000248075
TEST OBJECT_TYPE 42 .271207855
TEST CREATED 2305 .001608457
TEST LAST_DDL_TIME 2369 .001566737
TEST TIMESTAMP 2412 .001610251
TEST STATUS 2 .000009861
TEST TEMPORARY 2 .000009861
TEST GENERATED 2 .000009861
TEST SECONDARY 2 .000009861

13 rows selected.

3,表上列的直方图信息

SQL>
select table_name,column_name,endpoint_number,endpoint_value
from user_tab_histograms
where table_name = ‘TEST’
and column_name = ‘OBJECT_ID’;

TABLE_NAME COLUMN_NAM ENDPOINT_NUMBER ENDPOINT_VALUE
———- ———- ————— ————–
TEST OBJECT_ID 0 2
TEST OBJECT_ID 1 5160
TEST OBJECT_ID 2 10587
TEST OBJECT_ID 3 15658
TEST OBJECT_ID 4 20729
TEST OBJECT_ID 5 25800
TEST OBJECT_ID 6 30870
TEST OBJECT_ID 7 35940
TEST OBJECT_ID 8 41089
TEST OBJECT_ID 9 46821
TEST OBJECT_ID 10 53497

4,分区的统计信息

SQL>
select partition_name,num_rows,blocks,empty_blocks,avg_space
from user_tab_partitions
where table_name = ‘TEST’;

PARTITION_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
————— ———- ———- ———— ———-
P1 9581 140 0 0
P2 9973 164 0 0
P3 10000 158 0 0
P4 21151 326 0 0

5,分区上列的统计信息

SQL> select column_name,num_distinct,density,num_nulls
from user_part_col_statistics
where table_name = ‘TEST’
and partition_name = ‘P1’;

COLUMN_NAME NUM_DISTINCT DENSITY NUM_NULLS
————— ———— ———- ———-
OWNER 7 .000052187 0
OBJECT_NAME 7412 .000156925 0
SUBOBJECT_NAME 26 .47017301 9496
OBJECT_ID 9581 .000104373 0
DATA_OBJECT_ID 1765 .000664385 7780
OBJECT_TYPE 34 .18494854 0
CREATED 913 .001977449 0
LAST_DDL_TIME 994 .001882695 0
TIMESTAMP 982 .001928775 0
STATUS 2 .000052187 0
TEMPORARY 2 .000052187 0
GENERATED 2 .000052187 0
SECONDARY 1 .000052187 0

6,分区上列的直方图信息

SQL> select column_name,bucket_number,endpoint_value
from user_part_histograms
where table_name = ‘TEST’
and partition_name = ‘P1’
and column_name = ‘OBJECT_ID’;

COLUMN_NAME BUCKET_NUMBER ENDPOINT_VALUE
————— ————- ————–
OBJECT_ID 0 2
OBJECT_ID 1 1005
OBJECT_ID 2 1963
OBJECT_ID 3 2921
OBJECT_ID 4 3888
OBJECT_ID 5 4859
OBJECT_ID 6 5941
OBJECT_ID 7 6899
OBJECT_ID 8 7885
OBJECT_ID 9 8864
OBJECT_ID 10 9999

7,子分区的统计信息

SQL> select subpartition_name,num_rows,blocks,empty_blocks
from user_tab_subpartitions
where table_name = ‘TEST’
and partition_name = ‘P1’;

SUBPARTITION_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
—————————— ———- ———- ————
SYS_SUBP21 3597 50 0
SYS_SUBP22 3566 52 0
SYS_SUBP23 637 11 0
SYS_SUBP24 1781 27 0

8,子分区上的列的统计信息

SQL> select column_name,num_distinct,density
from user_subpart_col_statistics
where table_name = ‘TEST’
and subpartition_name = ‘SYS_SUBP21’;
COLUMN_NAME NUM_DISTINCT DENSITY
————— ———— ———-
OWNER 6 .000139005
OBJECT_NAME 3595 .000278319
SUBOBJECT_NAME 4 .014285714
OBJECT_ID 3597 .000278009
DATA_OBJECT_ID 155 .006451613
OBJECT_TYPE 8 .000139005
CREATED 751 .002392334
LAST_DDL_TIME 784 .002302524
TIMESTAMP 768 .00235539
STATUS 1 .000139005
TEMPORARY 2 .000139005
GENERATED 2 .000139005
SECONDARY 1 .000139005

9,子分区上的列的直方图信息

SQL> select column_name,bucket_number,endpoint_value
from user_subpart_histograms
where table_name = ‘TEST’
and subpartition_name = ‘SYS_SUBP21’
and column_name = ‘OBJECT_ID’;
COLUMN_NAME BUCKET_NUMBER ENDPOINT_VALUE
————— ————- ————–
OBJECT_ID 0 208
OBJECT_ID 1 1525
OBJECT_ID 2 2244
OBJECT_ID 3 2892
OBJECT_ID 4 3252
OBJECT_ID 5 4047
OBJECT_ID 6 5238
OBJECT_ID 7 6531
OBJECT_ID 8 7661
OBJECT_ID 9 8474
OBJECT_ID 10 9998

我们对这个复合分区分析之后产生了上面这九种不同层次的统计信息。CBO想要得要一个高效的执行计划需要如此多的统计信息.