lucene 缺点总汇

如果你的英语不错读一下这篇文章 ,关于全文检索的。
This post is about why Lucene may not be the best choice for future developments if nothing is done, and why the situation may not be close to change. In our situation, we push Lucene to its limits, although we make it work quite good. It's a reason why we made some suggestions and submitted a patch to Lucene (which does not cover everything listed here) : Lingway uses semantics to generate complex queries where proximity matters. For example, if you are looking for documents on conflicts in middle east , you'll probably also want to find documents talking about war in Iraq . In that case, war and Iraq are called expansions of conflict and middle east respectively. We provide a technology which analyzes your query in order to deduce the most relevant expansions, and generate queries for them. Yet, in order to get relevant results, this is insufficient : Google-like ranking or term frequency scoring like implemented in Lucene do not suit semantic scoring needs. For example, a document which contains both middle and east terms but separated by more than 1 word are most likely not what you want to find. Moreover, we should attribute lower scores on expansions than on the regular words. For example, we'll give a better score to conflict in middle east phrase than in war in Iraq .
——————————————————————————————

——————————————————————————————————————————————
1.               倒排中以docid排序,这样做的好处是多关键词查询时,merge算法自然,高效.支持phrase query; index merge阶段,处理简单.文件定位快速,倒排压缩高效.但是,它的一个致命的缺陷在于:当某个term的倒排很长时,在处理一次search时,系统需要对倒排所有元素都进行处理.这样的代价是不可接受的.这就注定了lucene不适合海量数据的检索(当然Local partition的分布式索引可以缓解这样的问题).大量的文献建议采用与query无关的ranking项进行排序.这样一方面可以对倒排剪枝,另一方面加速search.但这样的方法也有诸如:多关键词结果merge时的低效,索引merge的算法复杂,建立索引的代价大等缺点.克服这些不足,需要对这两者的优缺点进行相互的扬弃.目前考虑的是采用block的方法,即倒排中以block为基本单位,block之间是ranking降序,而 block内采用docid排序.具体细节这里不详细展开.

2.          频繁update的数据将使lucene对disk io影响巨大.lucene的增量索引是通过它的merge算法来实现的.而该merge算法导致频繁的disk操作.一个新的数据的update,可能导致一部分根本没有变化的索引被重写很多次,并且可能导致很多的小的index segment,造成了search的性能下降,当然,用户可以通过调节几个参数来缓解这个问题.我们可以,兼顾索引效率和检索效率,来重新设计 merge算法(中科院的firtex进行了部分尝试,不过缺点依然明显),可以设计Merge算法对于小的索引可以”越级”与大索引块进行合并,来减少 disk io.根据倒排block设计的思路,我们可以根据某些经验的统计量为每个block预留一定空间,每个单元有标记.这样,我们可以在一定程度上进行 update而根本不需要重写部分索引,从而大大减少disk io.当有大量数据update时候,再采用segment合并的算法进行合并.同时每个block都应该有block head,保留Block的一些统计信息,以便在search的时候及早剪枝.

3。再挑挑刺,Lucene结构很清爽。但唯独一个docid排序,这个假设,遍布与整个代码。惨不忍睹。

4。incremental fetch。lucene不支持从中间取索引。例如:用户取第十页,lucene需要把前面所有的内容都要检索出,然后所有的排序,过滤掉前面的然后返回。虽然说,这个从用户行为来说(因为大多数用户还是看前面的,不会跳着来),不是什么大问题。但是,这个毕竟可以解决。

5。lucene用java写。但是clucene为了保持与java lucene一致,用了很多难看的写法。并且更新不及时。

6。scorer 和weight写的比较难看。:)

7。doc-partition的模式,当然这个不是lucene本身的问题。doc- partition的方法有着很多不足,诸如全局统计量不准确,disk access大等等,但是大部分文章在综合了系统构架的简单性,网络负载和负载均衡还是普遍认为doc-partition比较优秀(google就是这种架构),当然针对doc模式的种种不足,也有很多的paper提出了改进的方法。我比较关注的是collection selection function, query log -based partition 和 hybrid architecture。

一 lucene文件的基本构架

      lucene文件结构的最大特点是其结构十分紧凑。从文件开始的第一个字节直到最后一个字节都是有效数据,中间没有任何空闲的字节。这样有优点也有缺点,优点是读取迅速,缺点是修改复杂。因为lucene的作者说lucene并不是为修改频繁的应用设计的,所以,文件结构这么做是无可厚非的。在修改频繁的环境下,lucene的性能注定会很差。如果是那样的话,您或许需要考虑使用更好的技术,因为增加一个文档到索引其实可以做到十分迅速。

      在压缩方面,lucene也采用了一些基本的方法。比如,它对int类型就进行了所谓的byte压缩方法(最初级的方法)。不过,它在String上面采用的utf-8的编码显然会比utf-16编码占用更多的空间。其它地方还能够看到压缩的是Field Data(域值,.fdt)文件,这个文件保存的是文档包含的域的具体文本(一个文档可以划分为多个域,每个域都是一个字符串),显然这是很大的数据(zlib好像在这里比较常用,google据说也这样压缩,不过,文本压缩的最好办法显然不是zip,更好的办法还有ppmd)。

————————————————————————————————————————————————————————————————————————————-

lucene构建索引的性能

      索引,专业点说,包含2种:前向索引和反向索引(倒排索引,inverted index)。前者表示的是某个文档里面的所有词语,后者表示的是包含某个词语的所有文档。对应到Lucene上面,它的前向索引可以认为是Term Vectors(词语向量)相关文件,包含.tvx、.tvd和.tvf这3种文件。前向索引没有什么好评论的,它一般只是做为重组原始数据时候的依据,其构建十分简单明了。反向索引对应到Lucene上就是index(索引)。Lucene把索引划分成一个一个的segment(块,其实是一个小索引),直观的说,当有一批新数据到达的时候,我们一般给其构建成一个新的segment,这是因为修改原来的segment的代价很高(并不是说一定很高,只是lucene采用的文件结构无法简单的加入新的文档)。当一个index包含的segment太多的时候,查找性能就很差了(因为一次查询需要查询多个segment),需要进行segment的合并。

      下面是index和segment的基本结构:

1.         index:

index包含4类文件:1)记录segment信息的文件;2)指示索引是否正在更改的标记文件;3)简单组合了若干个文件的复杂文件;4)segment文件及其附属文件。

2.         segment:

segment其实是一个小型index,它包含了词汇表、域表、反向索引表、域权重表、词语向量(即前向索引)和已经删除文档表。词汇表包括了本segment里面出现的所有词汇(记得词汇不见得是真的词语,它其实就是索引的字符串)。



三 lucene修改和删除索引的性能

      严格的说,lucene底层并不支持对某个文档的修改。因为它的紧密结构抗拒了对文档的直接修改。当需要修改某些文档的时候,可以是这样的:

1.         删除这些文档。这样会使得这些文档ID加入到已经删除的文档表里面。

2.         构建新的索引。这样会生成一个新的segment。

3.         合并索引的所有segment。这样会把所有的segment都合并到一起,构成唯一的一个segment。

大家可以看到,如果仅仅从以上3步来看,lucene的修改索引的性能极差。好在可以利用缓冲,分批的懒惰的进行上面的第2步和第3步。



四 lucene的查询性能

      我们从几个方面来分析它的查询性能:

1.         文件个数。文件个数越多,查询的时候需要访问的文件就越多,从而开销也会越大。这是因为要读取的类似数据处在不连续的位置。当你把所有segment都合并成一个之后,这种问题就不存在了。可是,合并segment的花销很大,需要谨慎考虑。

2.       索引词汇。lucene的词汇其实并不是简单的词汇,而是“域+词汇”的保存形式。当域比较多的时候,这种方式的索引词汇构建方式显然会大大降低查找的效率。不过,值得一提的是,为了降低空间占用,lucene在排序词汇之后,按照如下的形式进行保存: <PrefixLength, Suffix, FieldNum>,这里,PrefixLength表示本词汇借用了前面一个词汇的前面PrefixLength个字符,Suffix表示本词汇余下的字符串,FieldNum表示本字符串属于的域。

3.         布尔表达式计算。布尔表达式查找的时候,涉及到几条词汇倒排索引的合并的问题。未压缩的索引合并是一个十分容易(不过,算法需要很精细才能优化各种情况)的事情,可是,lucene的索引经过压缩了(包括前面提到的和相邻数据相减的压缩方法)以及String长度的不确定性,所以,我们无法根据词汇直接定位到它对应的TermInfo(做为一个变型,你可以在内存中为它做个索引)。于是lucene就使用了SkipInterval/SkipData(桩,即定位标记)这类结构来加快比较速度,通过和它们的比较,可以简单的跳过多个字节,从而加快了查找速度。当然了,这种策略比起直接的排序后2分查找显然是慢了许多。

4.         权重计算。权重的计算显然和文件结构没有太大关系。但是,已知的是,lucene保存了每个词汇的出现频率和每个域的权重值,这样就可以通过一些简单的公式计算满足要求的文档对本次查询的匹配度了。



五 Nutch对lucene的改进

      Nutch据说还是lucene的作者写的,不过,这次这个高手打算直接和商业搜索引擎进行抗衡,他引入了分布式的构架。Nutch一开始就是分布式的,它本来就是定位在百以上量级的集群系统(或者网格)上的。对于搜索引擎来说,除了抓取(或者还包含一些前期的数据处理)外,其余的工作都是信息保存、索引构建和索引查找。Nutch使用的分布式构架,它利用了多台机器的性能来同时构建索引(这一点的可行性在讲MapReduce的google论文里面已经做了详细的描述),这显然能够提高做索引的速度。在索引查找上面,因为索引查找显然不同于做索引,它要求极高的速度和不高的精度。简单的基于MapReduce的方法的最大缺点就是速度慢(因为它简单嘛),所以,这位高手强烈建议不要使用分布式的查找方法,因为速度比单机查找还要慢很多(考虑一下,对于google来说,它的数据量据说达到上百个T,即10万G,没有机器可以挂上这么大的硬盘吧?所以,他们肯定是分布式查询的)。可以肯定的是,Nutch在搜索方面对lucene的改进就是分布式的做索引。当然了,Nutch比lucene好的地方在于它有了抓取程序(虽然十分的原始)。
-- _____________________________________________________________________________________________________________________________
6、Lucene 的内建不支持群集。
Lucene是作为嵌入式的工具包的形式出现的,在核心代码上没有提供对群集的支持。实现对Lucene的群集有三种方式:1、继承实现一个 Directory;2、使用Solr 3、使用 Nutch+Hadoop;使用Solr你不得不用他的Index Server ,而使用Nutch你又不得不集成抓取的模块;

5、区间范围搜索速度非常缓慢;
Lucene的区间范围搜索,不是一开始就提供的是后来才加上的。对于在单个文档中term出现比较多的情况,搜索速度会变得很慢。因此作者称Lucene是一个高效的全文搜索引擎,其高效仅限于提供基本布尔查询 boolean queries;
4、排序算法的实现不是可插拔的,因为贯穿Lucene的排序算法的tf/idf 的实现,尽管term是可以设置boost或者扩展Lucene的Query类,但是对于复杂的排序算法定制还是有很大的局限性;
3、Lucene的结构设计不好;
Lucene的OO设计的非常糟,尽管有包package和类class,但是Lucene的设计基本上没有设计模式的身影。这是不是c或者c++程序员写java程序的通病?
A、Lucene中没有使用接口Interface,比如Query 类( BooleanQuery, SpanQuery, TermQuery...) 大都是从超类中继承下来的;
B、Lucene的迭代实现不自然: 没有hasNext() 方法, next() 返回一个布尔值 boolean然后刷新对象的上下文;
2、封闭设计的API使得扩展Lucene变得很困难;
参考第3点;
1、Lucene的搜索算法不适用于网格计算;
www.zhouxinxin.com 王不留行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值