[20160616]IOT与主外键.txt
--许多人都知道主外键引起的阻塞或者死锁,如果主表是IOT呢? IOT实际上一种特殊的索引结构,测试看看.
1.环境:
SCOTT@book> @ &r/ver1
PORT_STRING VERSION BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx 11.2.0.4.0 Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
create table p (
id number(10) primary key,
v varchar2(10) not null
) organization index;
create table c (
id number(10) primary key,
p_id number(10) not null references p(id),
v varchar2(10) not null
);
insert into p values (1, '1');
insert into p values (2, '2');
insert into c values (1, 1, '1');
insert into c values (2, 1, '2');
insert into c values (3, 2, '3');
create index cpid on c (p_id);
--注意建立了外键索引.
2.测试:
--session 1:
SCOTT@book> @ &r/spid
SID SERIAL# SPID PID P_SERIAL# C50
---------- ---------- ------ ------- ---------- --------------------------------------------------
232 1775 61605 21 53 alter system kill session '232,1775' immediate;
SCOTT@book> SELECT * FROM p WHERE id=1 FOR UPDATE;
ID V
---------- ----------
1 1
SCOTT@book> @ &r/xid
XIDUSN_XIDSLOT_XIDSQN
------------------------------
10.4.44155
--session 2:
SCOTT@book> @ &r/spid
SID SERIAL# SPID PID P_SERIAL# C50
---------- ---------- ------ ------- ---------- --------------------------------------------------
14 1579 61619 25 236 alter system kill session '14,1579' immediate;
SCOTT@book> INSERT INTO c (id, p_id, v) VALUES (12, 2, 'not locked');
1 row created.
--ok!没有问题.
SCOTT@book> INSERT INTO c (id, p_id, v) VALUES (11, 1, 'locked');
--挂起!!!
SCOTT@book> @ &r/viewlock
SID SERIAL# USERNAME OSUSER MACHINE MODULE LOCK_TYPE MODE_HELD MODE_REQUE LOCK_ID1 LOCK_ID2 OWNER OBJECT_TYP OBJECT_NAME BLOCK LOCKWAIT
------ ---------- ---------- ---------- ---------- ------------ --------------- ---------- ---------- ---------- ---------- ------ ---------- -------------------- ----- --------------------
14 1579 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94325 0 SCOTT TABLE P No 00000000851E4548
14 1579 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94327 0 SCOTT TABLE C No 00000000851E4548
14 1579 SCOTT oracle gxqyydg4 SQL*Plus TX Transaction None Share 655364 44155 No 00000000851E4548
14 1579 SCOTT oracle gxqyydg4 SQL*Plus TX Transaction Exclusive None 589844 13261 No 00000000851E4548
232 1775 SCOTT oracle gxqyydg4 SQL*Plus TX Transaction Exclusive None 655364 44155 Yes
232 1775 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94325 0 SCOTT TABLE P No
6 rows selected.
--为什么呢?我的理解因为IOT是特殊的索引,也就是P表实际上段不存在,仅仅存在索引段.
SCOTT@book> select * from dba_segments where owner=user and segment_name='P';
no rows selected
SCOTT@book> select * from dba_objects where owner=user and object_name='P';
OWNER OBJECT_NAME SUBOBJECT_ OBJECT_ID DATA_OBJECT_ID OBJECT_TYP CREATED LAST_DDL_TIME TIMESTAMP STATUS T G S NAMESPACE EDITION_NAME
------ -------------------- ---------- ---------- -------------- ---------- ------------------- ------------------- ------------------- ------- - - - ---------- ------------------------------
SCOTT P 94325 TABLE 2016-06-08 10:26:18 2016-06-08 10:26:18 2016-06-08:10:26:18 VALID N N N 1
SCOTT@book> select owner,SEGMENT_NAME,HEADER_FILE,HEADER_BLOCK,BYTES,blocks from dba_segments where owner=user and segment_name like '%94325';
OWNER SEGMENT_NAME HEADER_FILE HEADER_BLOCK BYTES BLOCKS
------ -------------------- ----------- ------------ ---------- ----------
SCOTT SYS_IOT_TOP_94325 4 530 65536 8
SCOTT@book> alter system checkpoint;
System altered.
--也就是数据在索引段.通过bbed观察:
BBED> set dba 4,531
DBA 0x01000213 (16777747 4,531)
BBED> p /d ktbbh.ktbbhitl[1]
struct ktbbhitl[1], 24 bytes @68
struct ktbitxid, 8 bytes @68
ub2 kxidusn @68 10
ub2 kxidslt @70 4
ub4 kxidsqn @72 44155
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~可以发现在索引段出现一个事务槽,与前面的对应.
struct ktbituba, 8 bytes @76
ub4 kubadba @76 12583551
ub2 kubaseq @80 8662
ub1 kubarec @82 14
ub2 ktbitflg @84 1 (NONE)
union _ktbitun, 2 bytes @86
sb2 _ktbitfsc @86 0
ub2 _ktbitwrp @86 0
ub4 ktbitbas @88 0
--当插入INSERT INTO c (id, p_id, v) VALUES (11, 1, 'locked');时,要检查P的主键是否有事务存在,而P表实际上是IOT表,这样lck的
--标识实际是在索引上.如果换成堆表就不存在这个问题了.
3.继续做一个特殊的例子:
--drop table cheap purge;
--drop table pheap purge;
create table pheap (
id number(10) ,
v varchar2(10) not null
);
--先建立索引,注意不唯一.并且包含字段v.再建立约束.
create index pk_pheap on scott.pheap (id,v);
alter table pheap add constraint pk_headp primary key (id) enable validate;
create table cheap (
id number(10) primary key,
p_id number(10) not null references pheap(id),
v varchar2(10) not null
);
insert into pheap values (1, '1');
insert into pheap values (2, '2');
insert into cheap values (1, 1, '1');
insert into cheap values (2, 1, '2');
insert into cheap values (3, 2, '3');
create index cheappid on cheap (p_id);
--session 1:
SCOTT@book(80,113)> update pheap set id=1 ,v='a' WHERE id=1 ;
1 row updated.
SCOTT@book(80,113)> @ &r/xid
XIDUSN_XIDSLOT_XIDSQN
------------------------------
6.29.4198
--session 2:
SCOTT@book(101,57)> INSERT INTO cheap (id, p_id, v) VALUES (12, 2, 'not locked');
1 row created.
SCOTT@book(101,57)> INSERT INTO cheap (id, p_id, v) VALUES (11, 1, 'locked');
--挂起!
SYS@book> @ &r/viewlock
SID SERIAL# USERNAME OSUSER MACHINE MODULE LOCK_TYPE MODE_HELD MODE_REQUE LOCK_ID1 LOCK_ID2 OWNER OBJECT_TYP OBJECT_NAME BLOCK LOCKWAIT
------ ---------- ---------- ---------- ---------- ------------ --------------- ---------- ---------- ---------- ---------- ------ ---------- -------------------- ----- --------------------
80 113 SCOTT oracle gxqyydg4 SQL*Plus TX Transaction Exclusive None 393245 4198 Yes
80 113 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94505 0 SCOTT TABLE PHEAP No
80 113 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94507 0 SCOTT TABLE CHEAP No
101 57 SCOTT oracle gxqyydg4 SQL*Plus TX Transaction Exclusive None 655379 44196 No 00000000851E40E8
101 57 SCOTT oracle gxqyydg4 SQL*Plus TX Transaction None Share 393245 4198 No 00000000851E40E8
101 57 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94507 0 SCOTT TABLE CHEAP No 00000000851E40E8
101 57 SCOTT oracle gxqyydg4 SQL*Plus TM DML(TM) Row-X (SX) None 94505 0 SCOTT TABLE PHEAP No 00000000851E40E8
7 rows selected.
--因为我修改pheap的索引,导致插入INSERT INTO cheap (id, p_id, v) VALUES (11, 1, 'locked');时阻塞.你可以转储索引看看.
SCOTT@book(80,113)> select owner,SEGMENT_NAME,HEADER_FILE,HEADER_BLOCK,BYTES,blocks from dba_segments where owner=user and segment_name like 'PK_PHEAP';
OWNER SEGMENT_NAME HEADER_FILE HEADER_BLOCK BYTES BLOCKS
------ -------------------- ----------- ------------ ---------- ----------
SCOTT PK_PHEAP 4 618 65536 8
SYS@book> alter system checkpoint;
System altered.
SYS@book> alter system dump datafile 4 block 619;
System altered.
Block header dump: 0x0100026b
Object id on Block? Y
seg/obj: 0x1712a csc: 0x03.1553b03e itc: 2 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x1000268 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 0x0006.01d.00001066 0x00c004a0.0739.09 ---- 2 fsc 0x0010.00000000
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Leaf block dump
===============
header address 140177634691684=0x7f7da61f0a64
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 3
kdxcosdc 0
kdxconro 3
kdxcofbo 42=0x2a
kdxcofeo 7990=0x1f36
kdxcoavs 7948
kdxlespl 0
kdxlende 1
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8032
row#0[8018] flag: ---D--, lock: 2, len=14
col 0; len 2; (2): c1 02
col 1; len 1; (1): 31
col 2; len 6; (6): 01 00 02 44 00 00
row#1[7990] flag: ------, lock: 2, len=14
col 0; len 2; (2): c1 02
col 1; len 1; (1): 61
col 2; len 6; (6): 01 00 02 44 00 00
row#2[8004] flag: ------, lock: 0, len=14
col 0; len 2; (2): c1 03
col 1; len 1; (1): 32
col 2; len 6; (6): 01 00 02 44 00 01
----- end of leaf block dump -----
End dump data blocks tsn: 4 file#: 4 minblk 619 maxblk 619
--注意~,xid与前面的可以对上.全表回滚,然后执行如下:
--session 1:
SCOTT@book(80,113)> update pheap set id=1 WHERE id=1 ;
1 row updated.
--//我仅仅修改id,而且前后数组没有变化.注如果执行这样,后面的结果也一样,不会阻塞.
--//SCOTT@book(80,113)> update pheap set id=1 ,v='1' WHERE id=1 ;
--//1 row updated.
SCOTT@book(80,113)> @ &r/xid
XIDUSN_XIDSLOT_XIDSQN
------------------------------
10.33.44192
--session 2:
SCOTT@book(101,57)> INSERT INTO cheap (id, p_id, v) VALUES (11, 1, 'locked');
1 row created.
--可以发现没有阻塞.
SYS@book> alter system checkpoint;
System altered.
SYS@book> alter system dump datafile 4 block 619;
System altered.
--检查转储内容:
Block header dump: 0x0100026b
Object id on Block? Y
seg/obj: 0x1712a csc: 0x03.1553b03e itc: 2 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x1000268 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 0x000a.01a.0000ac9d 0x00c00196.21dd.2c C--- 0 scn 0x0003.1553b019
--//可以发现索引上没有相关事务.因为前后修改的指一样.这样就很容易理解前面的SELECT * FROM p WHERE id=1 FOR UPDATE;另外的会
--//话插入INSERT INTO c(id, p_id, v) VALUES (11, 1, 'locked');会出现阻塞.就是因为IOT表本身就是索引结构,SELECT * FROM p
--WHERE id=1 FOR UPDATE;时,事务发生在相关索引段上.而堆表不会.
--当然在应用的表中使用IOT很少,出现主外键也许概率更低.不过理解后面的原理很重要.