oracle全文索引之同步和優化索引做了什麼

space6212發表於2019-04-20
一、同步索引做了什麼
我們知道,在資料被修改後,不能被查詢到了,直到索引被同步。那麼同步索引做了那些工作呢?


suk@oracle9i> insert into t_domain values(3,'this is beijing');

已建立 1 行。

suk@oracle9i> update t_domain set doc='this is beijing' where id=3;

已更新 1 行。

suk@oracle9i> delete from t_domain where id=5;

已刪除 1 行。

suk@oracle9i> commit;

提交完成。

suk@oracle9i> @begin_tracesuk@oracle9i> exec ctx_ddl.sync_index('IDX_DOMAIN');suk@oracle9i> @end_trace

摘取trace中主要的sql如下:

--發出索引同步命令
BEGIN ctx_ddl.sync_index('IDX_DOMAIN'); END;

--刪除DR$WAITING中與DR$PENGING重複的資訊
DELETE FROM DR$WAITING
WHERE
WTG_CID = :b1 AND WTG_PID = :b2 AND EXISTS (SELECT 1 FROM DR$PENDING
WHERE DR$PENDING.PND_CID = DR$WAITING.WTG_CID AND DR$PENDING.PND_PID =
DR$WAITING.WTG_PID AND DR$PENDING.PND_ROWID = DR$WAITING.WTG_ROWID )

這一步很重要,我們在《oracle全文索引之幾個關鍵表》中介紹了DR$WAITING的使用原理,為了避免重複同步同一條記錄的索引,提高效率,必須刪除掉DR$WAITING中多餘的資訊。
在刪除DR$WAITING的多餘資訊後,oracle會把DR$WAITING的記錄與DR$PENGDING的記錄聯合,得到最終需要同步的記錄的集合。
(因為這個實驗中DR$WAITING表中的資料完全是DR$PENDING的子集,所以,trace沒有反應合併這一步)

--通過一定的分詞演算法,把產生的資訊插入到$I表中
insert into "SUK"."DR$IDX_DOMAIN$I"
values
(:token, :ttype, :first, :last, :count, :data)

--插入記錄到$K表中
insert into "SUK"."DR$IDX_DOMAIN$K" (textkey, docid)
values
(:akeys, :adocs)

--更新$R表
select data
from
"SUK"."DR$IDX_DOMAIN$R" where row_no = :row_no for update
在trace中並沒有更新$R的直接資訊,但我猜測在同步索引時會更新$R表

--刪除DR$PENGING的資訊
delete from dr$pending
where
pnd_cid = :cid and pnd_pid = :pid and pnd_rowid = :rid

實際上,我猜測,如果刪除DR$WAITING中與DR$PENGING重複的資訊後,DR$WAITING還有記錄的話,這一步還會刪除DR$WAITIING的記錄的。

--最後提交
commit


二、優化索引做了什麼

優化索引可以有很多方式,不同的優化方式得到的結果會不一樣,但原理應該是差不多的。我們以FULL優化為例說明。
緊接著上面的例子,我們發出命令:

suk@oracle9i> @begin_tracesuk@oracle9i> exec ctx_ddl.optimize_index('idx_domain','FULL');suk@oracle9i> @end_trace

從trace檔案中摘取主要的sql:
--發出命令
BEGIN ctx_ddl.optimize_index('idx_domain','FULL'); END;

--將DR$INDEX的部分欄位設定為空
UPDATE DR$INDEX SET IDX_OPT_TOKEN= NULL ,IDX_OPT_TYPE= NULL ,IDX_OPT_COUNT=
NULL
WHERE
IDX_ID = :b1

其中:
IDX_OPT_TOKEN:當次優化的起始關鍵字
IDX_OPT_COUNT:當次優化的doc數量

--將$N表的doc設定為需要被優化狀態
update "SUK"."DR$IDX_DOMAIN$N" set nlt_mark = 'U'
where
nlt_mark != 'U'

之所以要做這一步是因為設定這些狀態和刪除$I表是兩個事務。為了避免在更新了狀態位後,沒有刪除$I表的相關資料的情況。

--將N$表的前16000行設定為"M"狀態
update "SUK"."DR$IDX_DOMAIN$N" set nlt_mark = 'M'
where
rownum <= 16000

"M"表示正在被處理。

--更新DR$INDEX的狀態
UPDATE DR$INDEX SET IDX_OPT_TOKEN=:b1,IDX_OPT_TYPE=:b2,IDX_OPT_COUNT=:b3,
IDX_DOCID_COUNT=GREATEST(IDX_DOCID_COUNT - :b3 ,0)
WHERE
IDX_ID = :b5

--提交
commit

--刪除無效的docid對應的$I中資料
delete from "SUK"."DR$IDX_DOMAIN$I"
where
rowid = :rid

--刪除已經優化完成的DOCID
delete from "SUK"."DR$IDX_DOMAIN$N"
where
nlt_mark = 'M'

從以上過程可以看出:oracle優化索引實際上是根據$N表記錄的DOCID刪除$I表對應的資訊,清除$I表的垃圾資料,從而達到提供查詢效能的目的。
實際上,優化索引還執行了合併token的步驟,減少索引的碎片。

在做full方式優化索引時有幾下幾點需要注意:
1)在9ir2以前的版本中,full方式優化索引相當於索引全部刪除了重建;在9ir2後,full方式優化索引不會去重寫已經被優化的記錄。
2)在9ir2前,每次用full方式優化索引最多能處理16000個docid,也就是說每次優化最多能在$I表刪除16000個docid對應的記錄,如果無效doc很多,需要多次執行優化才能完全優化。
在9ir2及以後版本,執行一次full方式優化可以刪除$I表全部的無效資訊,但是,在$N表每次還是最多刪除16000個docid(讓人容易混淆的地方,不明白oracle為什麼這樣做);
如果無效doc很多,那麼在執行完一次full優化後,如果不再產生新的無效doc,那麼,再之後的full優化中,只是刪除$N表的記錄。
3)DR$INDEX中的IDX_OPT_TOKEN為空表示索引已經執行完full方式的優化。


疑問:優化時,為什麼要分成兩個事務?

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/231499/viewspace-63757/,如需轉載,請註明出處,否則將追究法律責任。

相關文章