ITPub博客

首页 > Linux操作系统 > Linux操作系统 > oracle全文索引之commit与DML操作

oracle全文索引之commit与DML操作

原创 Linux操作系统 作者:space6212 时间:2019-06-06 15:24:05 0 删除 编辑
我们知道,无论对多大的数据做DML操作,执行commit都可以很快完成,但如何删除建有全文索引的记录,在commit时可能会很慢。
根据推断可以知道是由于域索引造成的,那么在有域索引的情况下,commit时,oracle还做了那些额外工作呢?


一、建立测试数据

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/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2005-01-25

  • 博文量
    245
  • 访问量
    161737