日期:2014-05-16  浏览次数:20650 次

Oracle如何高效抛出ora-00001错误
今天吃饭排队的时候,脑子突然闪出一个念头。设想一下以下场景:
在一张大表,可能有上亿条数据,在表中建立了主键或者唯一索引。当有会话插入数据违反唯一性约束时,Oracle是如何快速判断这个值已存在,而抛出ora-00001错误呢?
从性能上来讲,Oracle不太可能将这些数据存放在shared_pool或者其他内存区域中,假如放在内存区域中,必须先解决两个问题:
1、如果有上千个唯一约束表格,在内存中遍历上千表格的数据是不现实的,那性能问题怎么解决?
2、数据库刚启动时,Oracle绝大部分内存区域是空的,但实践证明,即使在数据启动不久,当业务有违反唯一性约束时,Oracle抛出ora-00001错误,依然迅速。
基于以上两点,我们可以反向推出Oracle肯定有另外的机制查找新更新或者插入的数据是否违反唯一性约束。
带着这些问题,进行如下实验:
1、首先创建测试表格
引用
SQL> create table t11 (id number,name varchar2(100));

Table created.


SQL> insert into t11  select rownum id,dbms_random.string('a',30) name from dual connect by level<=200000;

200000 rows created.

SQL> commit;

Commit complete.

增加唯一索引
引用
SQL> create unique index t11_idx on t11(id);

Index created

测试表格和索引的对象头如下:
引用
SQL> select HEADER_FILE,HEADER_BLOCK from dba_segments where SEGMENT_NAME='T11' and owner='ZHOUL';

HEADER_FILE HEADER_BLOCK
----------- ------------
         15        41867

SQL> select HEADER_FILE,HEADER_BLOCK from dba_segments where SEGMENT_NAME='T11_IDX' and owner='ZHOUL';

HEADER_FILE HEADER_BLOCK
----------- ------------
         15        51083

为观察结果,将buffer_cache清空
引用
SQL> alter system flush buffer_cache;

System altered.


采用10046进行跟踪

引用
SQL> ALTER SESSION SET EVENTS '10046 trace name context forever, level 12';

Session altered.

SQL> insert into t11 values(10,'test1');
insert into t11 values(10,'test1')
*
ERROR at line 1:
ORA-00001: unique constraint (ZHOUL.T11_IDX) violated


SQL> ALTER SESSION SET EVENTS '10046 trace name context off';

Session altered.




解析跟踪文件可以看到数据库执行的情况
引用
PARSING IN CURSOR #2 len=34 dep=0 uid=60 oct=2 lid=60 tim=1277628749310436 hv=3967311368 ad='277a7894'
insert into t11 values(10,'test1')
END OF STMT
PARSE #2:c=0,e=472,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=1277628749310431
BINDS #2:
WAIT #2: nam='db file sequential read' ela= 20 file#=15 block#=41867 blocks=1 obj#=1213118 tim=1277628749310583
WAIT #2: nam='db file sequential read' ela= 9 file#=15 block#=41866 blocks=1 obj#=1213118 tim=1277628749310624
WAIT #2: nam='db file sequential read' ela= 8 file#=15 block#=43402 blocks=1 obj#=1213118 tim=1277628749310656
WAIT #2: nam='db file sequential read' ela= 8 file#=15 block#=43506 blocks=1 obj#=1213118 tim=1277628749310686
WAIT #2: nam='db file sequential read' ela= 10 file#=15 block#=51084 blocks=1 obj#=1213119 tim=1277628749310771
WAIT #2: nam='db file sequential read' ela= 9 file#=15 block#=51085 blocks=1 obj#=1213119 tim=1277628749310804


41867块为T11表格段头,从段头中获取二级位图地址:0x03c0a38a
引用
Start dump data blocks tsn: 8 file#: 15 minblk 41867 maxblk 41867
buffer tsn: 8 rdba: 0x03c0a38b (15/41867)
scn: 0x0a00.10b5061c seq: 0x01 flg: 0x04 tail: 0x061c2301
frmt: 0x02 chkval: 0xd4ae type: 0x23=PAGETABLE SEGMENT HEADER
Hex dump of block: st=0, typ_found=1
。。。
  Segment Type: 1 nl2: 1      blksz: 8192   fbsz: 0     
  L2 Arra