一、建立测试数据
SQL> create table t_domain(id number,doc varchar2(100));
Table created
SQL> create index idx_domain on t_domain(doc) indextype is ctxsys.context;
Index created
SQL>
SQL> insert into t_domain values(1,'this is beijing');
1 row inserted
SQL> insert into t_domain values(2,'this is shanghai');
1 row inserted
SQL> commit;
Commit complete
SQL> exec ctx_ddl.sync_index('idx_domain');
PL/SQL procedure successfully completed
二、测试
1、delete数据
--刷新共享池
SQL> alter system flush shared_pool;
System altered
SQL> @d:sqlgettrace
TRACE_FILE_NAME
--------------------------------------------------------------------------------
E:oracleadminoracle9iudumporacle9i_ora_2580.trc
--删除前可以查出数据
SQL> select * from t_domain where contains(doc,'shanghai')>0;
ID DOC
---------- --------------------------------------------------------------------------------
2 this is shanghai
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered
--删除数据
SQL> delete from t_domain where id=2;
1 row deleted
--删除后执行同样的sql不能查出数据
SQL> select * from t_domain where contains(doc,'shanghai')>0;
ID DOC
---------- --------------------------------------------------------------------------------
SQL> commit;
Commit complete
SQL> alter session set events '10046 trace name context off';
Session altered
--提交后也不能查出数据
SQL> select * from t_domain where contains(doc,'shanghai')>0;
ID DOC
---------- --------------------------------------------------------------------------------
SQL>
E:oracleadminoracle9iudump>tkprof oracle9i_ora_2580.trc 2580.txt
摘取过程的DML如下:
--发出删除命令
delete from t_domain
where
id=2
--删除$K表的数据
DELETE "SUK"."DR$IDX_DOMAIN$K"
WHERE
TEXTKEY = :rid RETURNING DOCID INTO :docid
--向$N表插入数据
INSERT INTO "SUK"."DR$IDX_DOMAIN$N" (NLT_DOCID,NLT_MARK)
VALUES
(:docid,'U')
--向DR$DELETE表插入数据
INSERT INTO DR$DELETE (DEL_IDX_ID,DEL_IXP_ID,DEL_DOCID)
VALUES
(:idxid,:ixpid,:docid)
--提交
commit
--同步索引
begin ctxsys.syncrn(:idxid, :ixpid, :rtabnm); end;
--删除DR$DELETE表数据
delete from ctxsys.dr$delete
where
del_idx_id = :idxid and del_ixp_id = :ixpid
根据《oracle全文索引之如何实现查询》,提交前,oracle会在得到的结果中排除DR$DELETE存在的docid,所以在提交前不能查询到刚被更新的数据。
在commit时,oracle会同步索引,把$R表中已经被删除的docid在DATA字段对应的信息删除,所以,提交后,虽然$I表的信息没有被删除,但根据$R表的信息已经被删除,被update的数据在commit后是不会立刻能被查出来的,直到索引被同步。
同时可以看到,在commit后,还要执行一些delete操作来维护域索引,并且在执行commit同时同步全文索引,所以,可以推测commit不会立刻完成,什么时候能完成视删除的数据量而定。
2、插入记录
--刷新共享池
SQL> alter system flush shared_pool;
System altered
SQL> @d:sqlgettrace
TRACE_FILE_NAME
--------------------------------------------------------------------------------
E:oracleadminoracle9iudumporacle9i_ora_3712.trc
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered
SQL> insert into t_domain values(5,'this is shenzhen');
1 row inserted
SQL> commit;
Commit complete
SQL> alter session set events '10046 trace name context off';
Session altered
--插入数据后不能立刻查询新数据
SQL> select * from t_domain where contains(doc,'shenzhen')>0;
ID DOC
---------- --------------------------------------------------------------------------------
E:oracleadminoracle9iudump>tkprof oracle9i_ora_3712.trc 3712.txt
摘录trace文件中的DML语句如下:
--发出插入记录命令
insert into t_domain
values
(5,'this is shenzhen')
--把新记录信息插入到DR$PENDING中
INSERT INTO CTXSYS.DR$PENDING (PND_CID, PND_PID, PND_ROWID, PND_TIMESTAMP,
PND_LOCK_FAILED)
VALUES
(:1, :2, :3, SYSDATE, 'N')
commit
从以上的sql可以看出,在插入一条记录时,只是把新加记录的信息记录到DR$PENDING中,在commit前后没有同步索引,也就是在$I表中并没有新增的记录的信息,所以新增数据在commit前后都不能被查询到。
同时可以看出,,在commit后数据库并没有做其他工作,所以,可以推测无论插入多少数据,commit操作都可以很快完成。
3、更新非索引字段
--刷新共享迟
SQL> alter system flush shared_pool;
System altered
--得到当前会话对应的trace文件名
SQL> @d:sqlgettrace
TRACE_FILE_NAME
--------------------------------------------------------------------------------
E:oracleadminoracle9iudumporacle9i_ora_2032.trc
--更新前可以查询数据
SQL> select * from t_domain where contains(doc,'shanghai')>0;
ID DOC
---------- --------------------------------------------------------------------------------
2 this is shanghai
--设置10046
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered
SQL> update t_domain set id=2 where id=2;
1 row updated
SQL> select * from t_domain where contains(doc,'shanghai')>0;
ID DOC
---------- --------------------------------------------------------------------------------
2 this is shanghai
SQL> commit;
Commit complete
SQL> alter session set events '10046 trace name context off';
Session altered
SQL> select * from t_domain where contains(doc,'shanghai')>0;
ID DOC
---------- --------------------------------------------------------------------------------
2 this is shanghai
--更新后和commit后都可以查询原来的数据
格式化trace文件
E:oracleadminoracle9iudump>tkprof oracle9i_ora_2032.trc 2032.txt
把trace文件中执行过的DML sql(select除外)摘录如下:
update t_domain set id=2
where
id=2
commit
可见,在只更新非域索引字段的情况下,commit过程并没有做其他特别的事情,可以很快的完成。
被更新记录对应域索引的值没有发生变化,域索引也不需要维护,原来能查询出来的记录仍然可以被查出来。
4、更新索引字段
--刷新共享池
SQL> alter system flush shared_pool;
System altered
--得到trace文件名
SQL> @d:sqlgettrace
TRACE_FILE_NAME
--------------------------------------------------------------------------------
E:oracleadminoracle9iudumporacle9i_ora_3412.trc
--更新索引字段前可以查询数据
SQL> select * from t_domain where contains(doc,'beijing')>0;
ID DOC
---------- --------------------------------------------------------------------------------
11 this is beijing
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered
SQL> update t_domain set doc='this is beijing' where doc='this is beijing';
1 row updated
SQL> select * from t_domain where contains(doc,'beijing')>0;
ID DOC
---------- --------------------------------------------------------------------------------
SQL> commit;
Commit complete
SQL> alter session set events '10046 trace name context off';
Session altered
SQL> select * from t_domain where contains(doc,'beijing')>0;
ID DOC
---------- --------------------------------------------------------------------------------
--在更新索引字段后,无论是否commit都无法查找查询更新的内容
我们看看,在这段过程中到底执行了什么DML操作:
update t_domain set doc='this is beijing'
where
doc='this is beijing'
--删除$K表的docid相关信息
DELETE "SUK"."DR$IDX_DOMAIN$K"
WHERE
TEXTKEY = :rid RETURNING DOCID INTO :docid
--把删除的docid记录到$N表中
INSERT INTO "SUK"."DR$IDX_DOMAIN$N" (NLT_DOCID,NLT_MARK)
VALUES
(:docid,'U')
--把删除的docid记录到DR$DELETE中,为了预防用户在同一个会话中查询没有提交信息
INSERT INTO DR$DELETE (DEL_IDX_ID,DEL_IXP_ID,DEL_DOCID)
VALUES
(:idxid,:ixpid,:docid)
--把更新后的信息插入到DR$PENDING中
INSERT INTO CTXSYS.DR$PENDING (PND_CID, PND_PID, PND_ROWID, PND_TIMESTAMP,
PND_LOCK_FAILED)
VALUES
(:1, :2, :3, SYSDATE, 'N')
--提交
commit
--同步索引(只同步被删除的docid?)
begin ctxsys.syncrn(:idxid, :ixpid, :rtabnm); end;
这个过程第三个参数是$R表的表名。我猜测这一步是删除$R表中已经被删除的docid在DATA字段对应的信息。
--删除DR$DELETE中已经被同步的docid
delete from ctxsys.dr$delete
where
del_idx_id = :idxid and del_ixp_id = :ixpid
从上面sql可以看出,update索引列实际上就是先delete,再insert的过程。
这个例子中,被更新的值在更新前后是一样的,属于比较特殊的情况,但原理是一样的。
参看《oracle全文索引之如何实现查询》我们可以知道,提交前,oracle会在得到的结果中排除DR$DELETE存在的docid,所以在提交前不能查询到刚被更新的数据。
在commit时,oracle会同步索引,把$R表中已经被删除的docid在DATA字段对应的信息删除,
所以,提交后,虽然$I表的旧信息没有被删除,但由于$R表的信息已经被删除,所以更新前的数据是不会被查出来的,这样也避免了用户查到错误的数据。
以上是update过程中的delete部分;对于insert部分,其产生的信息仅存在于DR$PENDING中,$I还没有任何与之相关的信息,所以提交后更新后的数据也不会马上被查询出来。
同时可以看到,在commit后,还要执行一些delete操作来维护域索引,所以,可以推测,在update索引列后,执行commit并不会能立刻完成,什么时候能完成,视被更新的索引的记录多少而定。
总结:
1、基于域索引的insert执行commit可以很快完成,但在同步索引前不能查询新数据。
2、基于域索引的update操作,如果更新的是域索引列,在执行commit时还需要做其他工作,commit完成时间视更新数据的多少而定。但commit时不会同步索引,在同步索引前不能查询新数据。
3、基于域索引的update操作,如果更新的不是索引列,那么整个update过程与域索引无关,commit可以很快完成。
4、基于域索引的delete操作,在执行commit时需要同步索引和删除数据,故commit不能很快完成。commit的完成时间视删除的数据量而定。由于commit时同步了索引,故commit后被删除的数据也立刻不会再被查到。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/231499/viewspace-63758/,如需转载,请注明出处,否则将追究法律责任。