Index Rebuild Online 过程(9i)完整版

昨天晚上洗澡时新想到一个可以有效测试index rebuild online的方式, 也就是同时使用10046与10704 的trace event再配合lock阻塞的机制来测试index rebuild online的过程.

测试的过程如下.

--1. 构造一个500w条记录的表. 并创建需要rebuild online的索引.
--表的数量设置到这么大,,主要是为了给后续的操作留出手工运行的时间.
create table james_t as
select rownum id,dbms_random.string('l',20) user_name
from dual
connect by level <= 5e6;

create index james_t_pk on james_t (id);

--2. 在Session 2 (故意设置阻塞的进程)运行一个针对表james_t 的单条记录的更新.
update james_t set user_name = 'test 1' where rownum <= 1;

--3. 在Session 3中观察,当观察到session 1被堵塞在Table Share Lock的Request模式时进行下一步.
select /*+ rule*/a.* from v$lock a,v$session b where a.sid = b.sid and b.username = 'JAMES';

--4. 在Session 1 (运行index rebuild online的进程)运行如下语句.
alter session set events '10704 trace name context forever,level 10';
alter session set events '10046 trace name context forever,level 12';
alter index james_t_pk rebuild online;

--5. 在Session 2 提交前一个事务,使得rebuild online 过程继续,并运行一个需要大量index字段的更新操作.
--构造需要rebuild online在获取下一次Table Share lock之前需要Merge的数据.
commit;
update james_t set id = 5000000 + id where rownum <= 3e5;
commit;

--6. 在Session 2上运行一个类似于第二步的简单更新,意在阻塞Rebuild Online获取Table Share Lock.
update james_t set user_name = 'test 1' where rownum <= 1;

--7.  在Session 3中观察,当观察到session 1被堵塞在Table Share Lock的Request模式时,执行下面的语句后进行下一步.
--清空Buffer Cache以观察Session 1在获取Table Share Lock后执行的操作.(可供下载的Trace文件没有做这一步)
alter session set events = 'immediate trace name flush_cache';

--8. 提交Session 2的事务, 等待Rebuild Online结束..

--9. 从Session 3中提取出Session 1对应的Trace文件,,以及下面的v$session_longops 的结果.
SID	SERIAL#	OPNAME	TARGET	TARGET_DESC	SOFAR	TOTALWORK	UNITS	START_TIME	LAST_UPDATE_TIME
10	19	Sort Output			14759	14759	Blocks	07/28/2010 09:14:26	07/28/2010 09:14:41

1. 取得表上的Sub Share锁. 索引的object_id = 6399.
*** 2010-07-27 23:07:16.000
ksqcmi: TM,18fe,0 mode=2 timeout=21474836
ksqcmi: returns 0

2. 创建日志表.
"JAMES"."SYS_JOURNAL_6399"

create table "JAMES"."SYS_JOURNAL_6399" (C0 NUMBER, opcode char(1), partno number, rid rowid, primary key( C0 , rid ))
organization index TABLESPACE "USERS"

CREATE UNIQUE INDEX "JAMES"."SYS_IOT_TOP_6406" on "JAMES"."SYS_JOURNAL_6399"("C0","RID") INDEX ONLY TOPLEVEL TABLESPACE "USERS" NOPARALLEL

3. 请求表上的Share锁.
*** 2010-07-27 23:07:16.000
ksqcmi: TM,18fe,0 mode=4 timeout=21474836
WAIT #1: nam='enqueue' ela= 3072242 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072273 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072430 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3071962 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072350 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072367 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072086 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072453 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 387366 p1=1414332420 p2=6398 p3=0
ksqcmi: returns 0

2010-07-27 23:07:16.000 + 24.965529 = 2010-07-27 23:07:40.966
--此时间与下面获取Table Sub Share Lock的时间仅相差20ms左右. 中间的时间主要为统计误差,,因为Trace中的输出是连续的.

4. 取得完毕之后,,立即获取表上的Sub Share锁.
*** 2010-07-27 23:07:41.000
ksqcmi: TM,18fe,0 mode=2 timeout=21474836
ksqcmi: returns 0

5. 读取基础表,,创建索引.
WAIT #1: nam='db file scattered read' ela= 42228 p1=5 p2=4709 p3=4
WAIT #1: nam='db file scattered read' ela= 326 p1=5 p2=4710 p3=3
WAIT #1: nam='db file scattered read' ela= 236 p1=5 p2=4711 p3=2
.................
WAIT #1: nam='db file scattered read' ela= 549 p1=5 p2=36357 p3=4
WAIT #1: nam='db file scattered read' ela= 481 p1=5 p2=36358 p3=3

6. 开始Sort并输出索引. (写入临时文件)
WAIT #1: nam='direct path write' ela= 6 p1=201 p2=19942 p3=31
WAIT #1: nam='direct path write' ela= 2 p1=201 p2=19973 p3=4
WAIT #1: nam='direct path write' ela= 2 p1=201 p2=19721 p3=31
WAIT #1: nam='direct path write' ela= 395 p1=201 p2=19752 p3=12

--继续从表上读取内容.
WAIT #1: nam='db file scattered read' ela= 476 p1=5 p2=36359 p3=2
WAIT #1: nam='db file sequential read' ela= 417 p1=5 p2=36360 p3=1

WAIT #1: nam='direct path write' ela= 6 p1=201 p2=19942 p3=31
WAIT #1: nam='direct path write' ela= 2 p1=201 p2=19973 p3=4
WAIT #1: nam='direct path write' ela= 2 p1=201 p2=19721 p3=31
WAIT #1: nam='direct path write' ela= 395 p1=201 p2=19752 p3=12

--从临时文件读出排好序的结果.
WAIT #1: nam='direct path read' ela= 211 p1=201 p2=19763 p3=1
WAIT #1: nam='direct path read' ela= 57865 p1=201 p2=27472 p3=31
WAIT #1: nam='direct path read' ela= 14829 p1=201 p2=34281 p3=31
..........
WAIT #1: nam='direct path read' ela= 14853 p1=201 p2=20342 p3=19
WAIT #1: nam='direct path read' ela= 16932 p1=201 p2=26315 p3=31
WAIT #1: nam='direct path read' ela= 12710 p1=201 p2=21154 p3=31
WAIT #1: nam='direct path read' ela= 16599 p1=201 p2=32412 p3=31

写索引文件,扩展segment信息.

select file# from file$ where ts#=:1
select type#,blocks,extents,minexts,maxexts,extsize,extpct,user#,iniexts,NVL(lists,65535),NVL(groups,65535),cachehint,hwmincr, NVL(spare1,0) from seg$ where ts#=:1 and file#=:2 and block#=:3
insert into seg$ (file#,block#,type#,ts#,blocks,extents,minexts,maxexts,extsize,extpct,user#,iniexts,lists,groups,cachehint,bitmapranges,scanhint, hwmincr, spare1) values (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,0,0,:16,DECODE(:17,0,NULL,:17))
中间再夹杂部分
WAIT #1: nam='direct path read' ela= 20442 p1=201 p2=31559 p3=1

--结束Sort Output并使用Direct path write写入新索引.
WAIT #1: nam='direct path read' ela= 8504 p1=201 p2=19849 p3=1
WAIT #1: nam='direct path read' ela= 263 p1=201 p2=19974 p3=1
WAIT #1: nam='direct path read' ela= 46962 p1=201 p2=19721 p3=1
WAIT #1: nam='direct path write' ela= 359 p1=5 p2=48351 p3=7
WAIT #1: nam='direct path write' ela= 5 p1=5 p2=48358 p3=7

---在此时间点结束新索引的创建工作.
SID SERIAL# OPNAME TARGET TARGET_DESC SOFAR TOTALWORK UNITS START_TIME LAST_UPDATE_TIME
10 19 Sort Output 14759 14759 Blocks 07/28/2010 09:14:26 07/28/2010 09:14:41

7. 读取Journal表上的变更,,将变更Merge到新的索引上.
--从10046 的traced Event的角度看,,新的索引文件写完成,开始读取Journal表的内容,以merge新索引.

WAIT #1: nam='direct path read' ela= 23577 p1=201 p2=31718 p3=1
WAIT #1: nam='direct path read' ela= 60459 p1=201 p2=31877 p3=1
WAIT #1: nam='direct path write' ela= 5622 p1=5 p2=52496 p3=7
WAIT #1: nam='direct path write' ela= 3 p1=5 p2=52503 p3=2
WAIT #1: nam='db file sequential read' ela= 32390 p1=5 p2=397 p3=1
WAIT #1: nam='db file sequential read' ela= 34345 p1=5 p2=397 p3=1
WAIT #1: nam='db file sequential read' ela= 100004 p1=5 p2=52005 p3=1

--结束新索引的Merge工作.
WAIT #1: nam='db file sequential read' ela= 1521 p1=5 p2=32192 p3=1
WAIT #1: nam='db file sequential read' ela= 205 p1=5 p2=32192 p3=1
WAIT #1: nam='db file sequential read' ela= 252 p1=5 p2=32200 p3=1
WAIT #1: nam='db file sequential read' ela= 375 p1=5 p2=32200 p3=1

8. 请求表上的Share所.
--请求表上的Share 锁,,以准备结束索引重建..
*** 2010-07-28 09:15:17.000
ksqcmi: TM,18fe,0 mode=4 timeout=21474836
WAIT #1: nam='enqueue' ela= 3071546 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072536 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072024 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072293 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072416 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072140 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072175 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072294 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072249 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072318 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072184 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072216 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 3072247 p1=1414332420 p2=6398 p3=0
WAIT #1: nam='enqueue' ela= 1244788 p1=1414332420 p2=6398 p3=0
ksqcmi: returns 0

9. 读取刚刚阻塞Index Rebuild获取Share 锁所产生的Journal日志并将变更merge到索引上.

--说明,,由于Index Rebuild Online进程是做Enqueue Conversion,所以只可能有一个Session会阻塞此进程.
--此次需要Merge的变更量只是阻塞进程产生的变更量,因此一般情况下,,持有Share锁的时间比较短.
--但是会比第一次持有要稍长一点. 需要等后续清理对象的操作结束才能释放.

10. 删除Journal表
drop table "JAMES"."SYS_JOURNAL_6399"

9. 申请Journal表上的Exclusive 锁.
*** 2010-07-27 23:11:03.000
ksqcmi: TM,1906,0 mode=6 timeout=0
ksqcmi: returns 0
=====================

10. 结束索引重建,,修改相关数据字典表
--更新索引上的data_object_id.
update ind$ set ts#=:2,file#=:3,block#=:4,intcols=:5,type#=:6,flags=:7,property=:8,pctfree$=:9,initrans=:10,maxtrans=:11,blevel=:12,leafcnt=:13,distkey=:14,lblkkey=:15,dblkkey=:16,clufac=:17,cols=:18,analyzetime=:19,samplesize=:20,dataobj#=:21,degree=decode(:22,1,null,:22),instances=decode(:23,1,null,:23),rowcnt=:24,pctthres$=:31*256+:25, indmethod#=:26, trunccnt=:27,spare1=:28,spare4=:29,spare2=:30,spare6=:32where obj#=:1
bind 19: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=08 oacfl2=1 size=24 offset=0
bfp=032cc81c bln=24 avl=03 flg=05
value=6405
bind 33: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=08 oacfl2=1 size=24 offset=0
bfp=032cd8dc bln=22 avl=03 flg=05
value=6399

更新对象的data_object_id
update obj$ set obj#=:6,type#=:7,ctime=:8,mtime=:9,stime=:10,status=:11,dataobj#=:13,flags=:14,oid$=:15,spare1=:16, spare2=:17 where owner#=:1 and name=:2 and namespace=:3 and(remoteowner=:4 or remoteowner is null and :4 is null)and(linkname=:5 or linkname is null and :5 is null)and(subname=:12 or subname is null and :12 is null)

设置对象新关联的seg实体.通过ts#,header_file#,header_block#
update seg$ set type#=:4,blocks=:5,extents=:6,minexts=:7,maxexts=:8,extsize=:9,extpct=:10,user#=:11,iniexts=:12,lists=decode(:13, 65535, NULL, :13),groups=decode(:14, 65535, NULL, :14), cachehint=:15, hwmincr=:16, spare1=DECODE(:17,0,NULL,:17) where ts#=:1 and file#=:2 and block#=:3

执行Ojbect Checkpoint.
WAIT #1: nam='rdbms ipc reply' ela= 54 p1=5 p2=21474836 p3=0
WAIT #1: nam='rdbms ipc reply' ela= 44 p1=5 p2=21474836 p3=0

11. 到此索引Rebuild完成.

完整的Trace文件文件下载. james_ora_4268.trc.gz

Related posts:

  1. index rebuild online处理过程

12 comments to Index Rebuild Online 过程(9i)完整版

  • 咋变成墙内无法看了??

  • 这个链接墙内偶尔可看,,FeedSky已经完全不更新此我的blog了.

    查看Feed状态

    源地址:http://www.dbthink.com/?feed=rss2
    失败 : 2010-08-01 20:40:44 [603] : 连接被重置
    失败 : 2010-08-01 16:31:10 [603] : 连接被重置
    失败 : 2010-08-01 15:42:23 [603] : 连接被重置
    失败 : 2010-08-01 12:33:18 [603] : 连接被重置
    失败 : 2010-08-01 10:41:00 [603] : 连接被重置

    In.G 说:

    您好,您的源地址无法直接访问;国内不能访问到的地址我们也是不能抓取的,请您理解。

    这是FeedSky的解释,估计是国内已经控制了域名解析,,所以FeedSky已经有一段时间完全无法解析我的blog的Feed了.

    另外也有很多其它的feedsky的用户报告blog无法更新到feed上, 我估计是土共提高这一块的标准了.

    btw: 我的blog的域名/host都是在墙外的.

  • xuancan

    wangwang,丁原说的就是你啊!
    呵呵!!

  • 谢谢了。
    还有个参数,ksqcmi 的意思是?
    根据你的 这个例子 自己做了一遍。关于 TM,OD,DL 这些 ,应该关注哪个?
    http://space.itpub.net/22664653/viewspace-687897

  • 我这个测试主要是为了说明TM锁的,,
    1* select * from v$lock_type where type in (‘DL’,'OD’)
    SYS/SYS@mytest>/

    TYPE NAME ID1_TAG ID2_TAG IS_ DESCRIPTION
    —– —————————— ———- ———- — ————————————————–
    DL Direct Loader Index Creation object # 0 NO Lock to prevent index DDL during direct load

    OD Online DDLs object # 0 NO Lock to prevent concurrent online DDLs

    这两个锁,与rebuild online的过程直接相关,,在10g之前,是没有这两种锁类型的.

  • 谢谢了,学到很多东西,这几天回学校处理事情了。

  • zhangshirong

    您好,第9步中“说明,由于Index Rebuild Online进程是做Enqueue Conversion,所以只可能有一个Session会阻塞此进程.
    ”。如果此时多个会话同时对基表做DML操作,那rebuild online进程会被这多个DML会话阻塞啊?

  • 如果有多个session已经在事务中,,rebuild online进程确实会被阻塞的,,不过,,我的理解是从rebuild online进程开始申请enqueue conversion开始,新的dml进程就无法执行了..会被rebuild online进程阻塞,,不然rebuild online就会面临永远无法结束的困境了.

  • zhangshirong

    谢谢您的回复。有一个问题:是否所有enqueue的owner需要做enqueue conversion时(例如从SX->S)时,其都会阻塞后续的与其不兼容的锁请求,也就是做enqueue conversion的会话拥有更高的优先级?谢谢。

  • Enqueue conversion 是有更高的优先级啊..

  • zhangshirong

    谢谢。做了个实验:
    session 1:
    LOCK TABLE test IN ROW EXCLUSIVE MODE;

    session 2:
    LOCK TABLE test IN ROW EXCLUSIVE MODE;
    LOCK TABLE test IN SHARE MODE;

    session 3:
    LOCK TABLE test IN ROW EXCLUSIVE MODE;

    在session 1中commit,session 2获得 share lock.Enqueue conversion 拥有更高的优先级。

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>