Lucene一些问题总结(网上资料和自己累积)
<1>.昨天改了一个晚上代码都无法使搜索引擎创建文件索引到硬盘中,经过调试发觉已经把文档、字段提取到了内存索引器中,然而在把内存索引书写器中的索引传递给硬盘索引书写器时似乎没有传送成功,只出现segments.gen和segments_2。单独使用FSDirectory时,却是在硬盘相应目录中能够看到索引文件啊!fsWriter.addIndexesNoOptimize(Directory[]{ramdir});明明已经写上了这句了啊?第二天上午翻翻资料,发觉有一份资料强调:
“在合并内存索引器的索引到硬盘索引器前,务必先关闭内存索引器。”
于是我试着在前面加上"ramWriter.close();",果然终于出现了久违的.cfs文件。
<2>上周在使用范围搜索时又遇到问题,程序抛出TooManyClausesexception。后来才发现lucene将范围搜索转化为精确匹配,每个匹配对应一个clause,所以如果你的范围如果包含超过1024个索引值,程序就会抛错
方案1:BooleanQuery.setMaxClauseCount(Integer.MAX_VALUE)改变限制,这个可以解决问题,但有隐患,如果某个范围的索引特别多,内存会有爆掉的危险。
方案2:使用filter
<3>目前索引里面已经有1000多万的数据了,现在需要每几分钟就增量得添加新的内容到索引中。但是,我发现新加入索引后,整个索引结构都要重新调整。非常耗时(长达半个小时)。不知道大家有没有什么比较好的办法,加快这个过程?
回答:
我觉得用lucene做,一个原则索引里面尽量少存储,索引文件小了,optimize要移动的数据块也小。
还有Lucene实在不适合做实时,有一个办法,将新索引建在内存中,新建在内存上的searcher与硬盘索引searcher合并为MutliSearcher提供给前端搜索,
内存到达一定量时再后台合并到主索引上,合并完成用新的Searcher替换MutliSearcher。
RAMDirectory和FSDirectory相结合使用 //创建基于RAMDirectory的索引 RAMDirectory rd = new RAMDirectory(); IndexWriter iw = new IndexWriter(rd,new StandardAnalyzer()); …. //向基于RAMDirectory的索引中添加文档 iw.addDocument(doc); iw.close(); //建立基于FSDirectory的索引 FSDirectory fd = FSDirectory.getDirectory("mix"); IndexWriter writer = new IndexWriter(fd,new StandardAnalyzer()); //把缓存在RAMDirectory中的所有数据写入FSDirectory writer.addIndexes(new Directory[] {rd}); writer.close(); //同样可以反过来用,将文件系统中的索引读入内存中,就需要使用如下的方法: RAMDirectory rd=new RAMDirectory();
<4>在大规模的应用中,Lucene更适合用于狭义的“搜索”,而不应当负责数据的存储。我们看看Lucene的源代码也可以知道,Document和Field的存储效率是不够好看的。手机之家的团队也发现了这一点,他们的办法是,用Lucene存放索引,用Memcache+BerkeleyDB(JavaEdition)负责存储。这样有两个好处,一是减小了Lucene的数据规模,提高了程序的效率;另一方面,这套系统也可以提供某些类似SQL的查询功能。实际上,LuceneProject自己似乎也注意到了这个问题,在Store中新增了一个db选项,其实也是利用的BerkeleyDB。如果仅仅用Lucene存放索引,而不存放Document,并且合理配置,一台机器可以支持几十G甚至上百G的索引;如果需要用Lucene存放索引,最好在读取时使用FieldSelector,只读取需要的Field,如果使用恰当,性能会有10%左右的提升。
<5>在大规模应用中,Cache是非常重要的。PPT中也提到,可以在程序提供服务之前,进行几次”预热“搜索,填充Searcher的Cache。据我们(银杏搜索)的经验,也可以在应用程序中,再提供针对Document的Cache,这样对性能有较大的改善(同一个JVM内部的Cache,速度更快一些)。Lucene自己似乎也注意到了这个问题,在2.4版本中提供了Cache,并提供了一个LRUCache实现。不过据我们测试,在极端情况下,这个Cache可能会突破大小限制,一路膨胀最后吃光内存,甚至从网络上找的许多LRUCache实现在极端条件下都有可能出现这样的问题(这也是我们百思不得其解的地方:反复检查程序的逻辑都没有问题),最终自己写了一个LRUCache,并修改多次,目前来看是稳定的。
<6>在编写Java服务程序的时候,记得设置退出的钩子函数(RunTime.getRunTime.addShutdownHook)是一个非常好的习惯。许多Java程序员都没有这种意识,或者有,也只是写一个finalize函数,结果程序非正常退出时,可能造成某些外部资源的状态不稳定。拿Lucene来说,之前的IndexWriter是默认autoCommit的,这样每添加一条记录,就提交一次,好处是如果中断,则之前添加的记录都是可用的,坏处则是,索引的速度非常低。在新版本中autoCommit默认为False,速度提升明显(我们测试的结果是,提高了大约8倍),但如果中途异常退出,则前功尽弃。如果我们添加了退出的钩子函数,捕获到退出信号则自动调用writer.close()方法,就可以避免这个问题。
<7>目前的Lucene是兼容JDK1.4的,它的binary版本也是JDK1.4编译的,如果对性能要求比较高,可以自行下载LuceneSourceCode,用更新版本的JDK编译出.jar文件,据我测试,速度大约有30%的提升。
<8>如果对并发的要求较高,可以考虑采用多IndexSearcher的技术,也就是在一个应用服务中,开启多个IndexReader(可以对同样的索引开启多个),每个IndexReader再生成一个IndexSearcher,将这些Searcher放在一个“池”里头,给搜索请求调用。这样可以大幅度提高并发的性能,代价是在写程序的时候就要考虑到这一点,进行相应的调整。