由于工作需要笔者在HP UX Soralis 上面设置了Oracle Intermedia来实现全文检索目前已经投入实际使用设置过程中有许多问题和经验拿来和大家交流本文依据的是Oracle 和两个版本不能保证适用于其他版本
目前全文检索功能几乎所有主流数据库都支持此前笔者曾在sql server 上实现感觉非常简单方便但创建全文检索索引的时间比较长通常要十几个小时Oracle 的全文检索建立和维护索引都要快得多笔者的万记录的一个表建立索引只需要分钟同步一次只需要分钟但设置就要复杂得多
一设置过程
首先检查你的数据库是否安装了intermedia
这可以通过检查是否有ctxsys用户和ctxapp角色(role) 如果没有这个用户和角色意味着你的数据库创建时未安装intermedia功能你必须修改数据库以安装这项功能
修改过程
运行 $ORACLE_HOME/bin/dbassist 选择modify database 然后在选择数据库功能时将j server 和 intermedia 都选上(安装intermedia必须同时安装jserver)强烈建议你在做这个改动前先备份整个数据库
设置extproc
Oracle 是通过所谓的外部调用功能(external procedure)来实现intermedia的因此正确地设置extproc是关键一步
首先要配置listener 使它能监听intermedia 调用的请求你可以通过运行$ORACLE_HOME/bin/netassit 来进行配置也可以手工修改配置文件$ORACLE_HOME/network/admin/listenerora 然后重新启动listener下面以一个例子来讲述如何手工修改配置文件
打开listenerora文件在修改前通常有如下内容(假定使用缺省listener)
LISTENER = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = MYDATABASE)(PORT = )) ) SID_LIST_LISTENER = (SID_DESC = (GLOBAL_DBNAME = mydatabaseworld) (ORACLE_HOME = /u/app/oracle/product/) (SID_NAME = mydatabase) )
这个listener还没有配置extproc 因此需要为它增加对extproc的监听办法就是分别增加description 和 sid_desc 修改后的listnerora 如下
LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = MYDATABASE)(PORT = )) ) (DESCRIPTION = (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC)) ) ) SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (GLOBAL_DBNAME = mydatabaseworld) (ORACLE_HOME = /u/app/oracle/product/) (SID_NAME = mydatabase) ) (SID_DESC = (PROGRAM = extproc) (SID_NAME = PLSExtProc) (ORACLE_HOME = /u/app/oracle/product/) ) )
注意上面的host global_dbnamesid_nameoracle_home应填写你的数据库的实际值但program一项必须填写extproc
其次要配置服务器端的tnsnamesora文件该文件的位置在$ORACLE_HOME/network/admin下面同样可以通过运行netasst来进行配置
在tnsnamesora文件中需要增加如下一项
EXTPROC_CONNECTION_DATAEXTPROC_CONNECTION_DATAWORLD = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC)) ) (CONNECT_DATA = (SID = PLSExtProc) ) )
注意其中KEY 和SID必须与listenerora中的key 和sid_name对应相同
修改完成后重新启动listener (先用lsnrctl stop 然后 lsnrctl start) 然后使用tnsping 来测试一下是否配置正确
tnsping extproc_connection_data 或者
tnsping extproc_connection_dataworld如果配置正确会显示
Attempting to contact (ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC)) OK(毫秒)
否则请检查你的上述两个文件并注意在修改后一定要重新启动listener但并不需要重新启动数据库
设置词法分析器(lexer)
Oracle实现全文检索其机制其实很简单即通过Oracle专利的词法分析器(lexer)将文章中所有的表意单元(Oracle 称为 term) 找出来记录在一组 以 dr$开头的表中同时记下该term出现的位置次数hash 值等信息检索时Oracle 从这组表中查找相应的 term并计算其出现频率根据某个算法来计算每个文档的得分(score)即所谓的匹配率而lexer则是该机制的核心它决定了全文检索的效率Oracle 针对不同的语言提供了不同的 lexer 而我们通常能用到其中的三个
basic_lexer 针对英语它能根据空格和标点来将英语单词从句子中分离还能自动将一些出现频率过高已经失去检索意义的单词作为垃圾处理如if is 等具有较高的处理效率但该lexer应用于汉语则有很多问题由于它只认空格和标点而汉语的一句话中通常不会有空格因此它会把整句话作为一个term事实上失去检索能力以中国人民站起来了这句话为例basic_lexer 分析的结果只有一个term 就是中国人民站起来了此时若检索中国将检索不到内容
chinese_vgram_lexer 专门的汉语分析器支持所有汉字字符集该分析器按字为单元来分析汉语句子中国人民站起来了这句话会被它分析成如下几个term 中中国国人人民民站站起起来来了了可以看出这种分析方法实现算法很简单并且能实现一网打尽但效率则是差强人意
chinese_lexer 这是一个新的汉语分析器只支持utf字符集上面已经看到chinese vgram lexer这个分析器由于不认识常用的汉语词汇因此分析的单元非常机械像上面的民站站起在汉语中根本不会单独出现因此这种term是没有意义的反而影响效率chinese_lexer的最大改进就是该分析器 能认识大部分常用汉语词汇因此能更有效率地分析句子像以上两个愚蠢的单元将不会再出现极大 提高了效率但是它只支持 utf 如果你的数据库是zhsgbk字符集则只能使用笨笨的那个Chinese vgram lexer
如果不做任何设置Oracle 缺省使用basic_lexer这个分析器要指定使用哪一个lexer 可以这样操作
第一在ctxsys用户下建立一个preference
begin ctx_ddlcreate_preference(my_lexerchinese_vgram_lexer) end
第二在建立intermedia索引时指明所用的lexer
create index myindex on mytable(mycolumn) indextype is ntext
parameters(lexer my_lexer)
这样建立的全文检索索引就会使用chinese_vgram_lexer作为分析器
使用job定时同步和优化
在intermedia索引建好后如果表中的数据发生变化比如增加或修改了记录怎么办?由于对表所发生的任何dml语句都不会自动修改索引因此必须定时同步(sync)和优化(optimize)索引以正确反映数据的变化
在索引建好后我们可以在该用户下查到Oracle自动产生了以下几个表(假设索引名为myindex)
DR$myindex$IDR$myindex$KDR$myindex$RDR$myindex$N
其中以I表最重要可以查询一下该表看看有什么内容
select token_text token_count from DR$I_RSK$I where rownum<=
这里就不列出查询接过了可以看到该表中保存的其实就是Oracle 分析你的文档后生成的term记录在这里包括term出现的位置次数hash值等当文档的内容改变后可以想见这个I表的内容也应该相应改变才能保证Oracle在做全文检索时正确检索到内容(因为所谓全文检索其实核心就是查询这个表)那么如何维护该表的内容呢?总不能每次数据改变都重新建立索引吧!这就用到sync 和 optimize了
同步(sync)将新的term 保存到I表
优化(optimize)清除I表的垃圾主要是将已经被删除的term从I表删除
Oracle提供了一个所谓的ctx server来做这个同步和优化的工作只需要在后台运行这个进程它会监视数据的变化及时进行同步但笔者使用ctxserver碰到了许多问题Oracle 北京的support也建议不使用而是用以下的两个job来完成(该job要建在和表同一个用户下)
sync: VARIABLE jobno number; BEGIN DBMS_JOBSUBMIT(:jobnoctx_ddlsync_index(myindex); SYSDATE SYSDATE + (//)); commit; END; optimizer VARIABLE jobno number; BEGIN DBMS_JOBSUBMIT(:jobnoctx_ddloptimize_index(myindexFULL); SYSDATE SYSDATE + ); commit; END;
其中 第一个job的SYSDATE + (//)是指每隔分钟同步一次第二个job的SYSDATE + 是每隔天做一次全优化具体的时间间隔你可以根据自己的应用的需要而定至此你的全文检索功能已设置完成
二常见错误
下面就一些常见的错误信息给出解释和解决办法
sync 失败
DRG ALTER INDEX T_DOC_CT失败
DRG oracle error in drsxsopen
ORA STR 赋值变量缺少空后缀
解决这是i的一个bug 但可以避免它方法是在同步之前先发一个语句
alter session set nls_language=American
create index 失败
ORA 执行 ODCIINDEXCREATE 例行程序时出错
ORA interMedia Text 错误
ORA 在CTXSYSDRUE line
ORA 在CTXSYSTEXTINDEXMETHODS line
ORA 在line
解决这是之前的版本的一个bug 在处理中文时某个特殊字符造成的向Oracle要补丁或者自己去 下载(需要CSI 号码)
create index 失败
RA 执行 ODCIINDEXCREATE 例行程序时出错
ORA interMedia Text 错误
DRG Net 监听器没有运行或无法启动外部过程
ORA 无法打开与外部过程代理程序的 RPC 连接
ORA 在CTXSYSDRUE line
ORA 在CTXSYSTEXTINDEXMETHODS line
ORA 在line
解决明显的extproc配置不当仔细阅读本文基本设置的第二步
访问建有索引的表时失败
ora 域索引标记为loading/failed/unusable
解决这是该表的一个intermedia索引有问题该索引要么没有正确建立要么是某次同步失败导致它状态异常先查到是哪个索引
Select idx_nameidx_status from ctxsysctx_indexes
然后同步该索引或者强制删除它
重建alter index myindex rebuild online parameters(sync)
删除drop index myindex force
使用chinese_lexer失败
ERROR at row
ORA err on ODCIINDEXCREATE
ORA interMedia Text err
DRG index is not existing
DRG the lexer cannt analyze as SIMPLIFIED CHINESE_CHINAZHSGBK
ORA 在CTXSYSDRUE line
ORA 在CTXSYSTEXTINDEXMETHODS line
ORA 在line
解决chinese_lexer 只支持utf字符集现在你面临抉择忍受chinese vgram lexer的愚蠢或者将数据库字符集改到 utf 但面对可能引起你的应用不能正确处理中文的风险(先咨询Oracle support 并且与你的应用软件提供商联系)
升级或应用patch后失败
ORA err when execute ODCIINDEXDROP
ORA interMedia Texterr
ORA PL/SQL can not find program unit beingcalled
ORA at CTXSYSDRUE line
ORA atCTXSYSTEXTINDEXMETHODS line
ORA at line
解决这是intermedia的某个object 没有正确产生或者编译用ctxsys用户登录后运行 $oracle_home/ctx/admin/drpkhsql 和 $oracle_home/ctx/admin/drplbsql 以重新产生所有的package你也可以直接察看dba_objects视图找出那些属于ctxsys用户并且status 为invalid的东西重新产生或者重新编译(你可能会发现有许多这种东西不要惊讶Oracle不会因此而崩溃)
create index 失败
ERROR 位于第 行
ORA 执行 ODCIINDEXCREATE 例行程序时出错
ORA interMedia Text 错误
DRG oracle error in driddlIndexResume
ORA 在尝试分配 字节 (cursor work heQERHJ Bit vector)时进程内存不足
ORA 在CTXSYSDRUE line
ORA 在CTXSYSTEXTINDEXMETHODS line
ORA 在line
解决引起这个问题可以有多种原因首先你可以将sort_area_size这个参数减小到不多于M这可以防止Oracle在创建索引时分配太多的sort 内存而耗尽资源 但如果这不起作用而且你是 则恭喜你hit 了bug 该bug 在你要建索引的字段如果某条记录的长度超过字符时引起Oracle耗尽内存资源别无它法除了打 B 的补丁