huangfox

冰冻三尺,非一日之寒!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

系列汇总:

lucene3.0_基础使用及注意事项汇总

 

part1:

本文介绍和IndexWriter有关的3个参数:

1.MAXBufferedDocs

 MaxBufferedDocs这个参数默认是disabled的,因为Lucene中还用另外一个参数(RAMBufferSizeMB)控制这个bufffer的索引文档个数。
其实MaxBufferedDocs和RAMBufferSizeMB这两个参数是可以一起使用的,一起使用时只要有一个触发条件满足就写入硬盘,生成一个新的索引segment文件。

2.RAMBufferSize

 控制用于buffer索引文档的内存上限,如果buffer的索引文档个数到达该上限就写入硬盘。当然,一般来说也只越大索引速度越快。当我们对文档大小不太确定时,这个参数就相当有用,不至于outofmemory error.

3.MegerFactor

SetMergeFactor是控制segment合并频率的,其决定了一个索引块中包括多少个文档,当硬盘上的索引块达到多少时,将它们合并成一个较大的索引块。当MergeFactor值较大时,生成索引的速度较快。MergeFactor的默认值是10,建议在建立索引前将其设置的大一些。

 

part2:

注意:

1.不要随意设置MaxbufferedDocs。

MaxBufferedDocs和RAMBufferSize共同控制内存中文档的容量。

如果对MaxBufferedDocs进行设置要比较小心了,因为它本身是disabled,如果设置不合理将导致大规模的重建索引非常慢。

例如:

 

dir = SimpleFSDirectory.open(new File("d:/20101015index"));
writer
=new IndexWriter(dir, new StandardAnalyzer(Version.LUCENE_30), true, MaxFieldLength.UNLIMITED);
writer.setUseCompoundFile(
true);
writer.setMaxBufferedDocs(
100);
writer.setMergeFactor(
10);
//add document
//注意点2:filed实例在多次添加的时候可以重用,节约构造field实例的时间。
Field f1 =new Field("f1", "", Store.YES, Index.ANALYZED) ;
Field f2
=new Field("f2", "", Store.YES, Index.ANALYZED) ;
for (int i =0; i <1000000; i++) {
Document doc
=new Document();
f1.setValue(
"f1 hello doc"+ i);
doc.add(f1);
f2.setValue(
"f2 world doc"+ i);
doc.add(f2);
writer.addDocument(doc);
}
writer.optimize();

 

上述代码中对MaxBufferedDocs进行设置:

 

writer.setMaxBufferedDocs(100);

那么现在对内存中文档的容量有两个控制量:

1.文档数量达到100就写回磁盘;

2.文档总容量达到16m就写回磁盘;

因为上面一个文档的大小很小,100个文档的容量肯定不会达到16m,所以第一个控制量起到主要的作用。

在没有设置MaxBufferedDocs之前就只有RAMBufferSize(16m)控制内存中文档的数量,

较之这两种情况,明显是没有设置MaxbufferedDocs更好的利用了内存,因此建立索引更快速。

实验证明:

不设置MaxbufferedDocs,耗时20s左右;

设置MaxbufferedDocs为100,耗时280000s左右。 = =! 差别太大了,所以一定要小心!!!

 

2.不要太迷信MegerFactor比较大会加快重建索引的速度。

通过实验,在上述代码中奖MegerFactor设置成10比100要快2s。

 

“一家之言”——

对于一般的系统(数据量在千万级,重建索引暂不考虑并发压力),在重建索引时不要太纠结这些参数的设置,

只要不犯太严重的问题(例如像上面一样将MaxbufferedDocs设置得过小,还不如不设置),效率出入都不会太大。

 

3.是否有必要将数据先放到RAM中,再和磁盘的索引进行合并?

我认为在重建索引的环节没有必要。因为在使用FSDirectory建立索引的时候不就可以控制内存的使用么!?(MaxbufferedDocs和RAMBufferSize)

而RAMDiretory应该重点使用在实时索引上面。

(= =! 我指的重建索引是什么意思?"对大量的数据一次性建立成磁盘索引!")

这里也做了一个测试,是先将文档写入内存,然后合并到磁盘。

主要代码如下所示:

 

dir =new RAMDirectory();
dirFS
= SimpleFSDirectory.open(new File("d:/20101015index"));
writer
=new IndexWriter(dir, new StandardAnalyzer(Version.LUCENE_30), MaxFieldLength.UNLIMITED);
writerFS
=new IndexWriter(dirFS, new StandardAnalyzer(Version.LUCENE_30), true, MaxFieldLength.UNLIMITED);
//
Field f1 =new Field("f1", "", Store.YES, Index.ANALYZED);
Field f2
=new Field("f2", "", Store.YES, Index.ANALYZED);
for (int i =0; i <1000000; i++) {
Document doc
=new Document();
f1.setValue(
"f1 hello doc"+ i);
doc.add(f1);
f2.setValue(
"f2 world doc"+ i);
doc.add(f2);
writer.addDocument(doc);
}
// writer.commit();
writerFS.addIndexes(writer.getReader());

 

经过测试,发现效率和直接写磁盘差不多!

 

刚开始学lucene的时候,总热衷提高效率,什么参数的设置啊、RAM的使用啊等等,殊不知重建索引是一个初始化阶段(服务于后期的检索),

就算有优化的必要,但绝不是当前必要解决的“主要矛盾”。

应该将重点放到:

1.实时索引;

2.分布式构建检索框架(如果系统规模有必要的);

3.怎么在程序质量上利用好内存,不至于运行时到处时 内存溢出 。

 

part3:

转载了一篇相关文章——

影响Lucene索引速度原因以及提高索引速度技巧

• 确认你在使用最新的Lucene版本。

• 尽量使用本地文件系统

远程文件系统一般来说都会降低索引速度。如果索引必须分布在远程服务器,请尝试先在本地生成索引,然后分发到远程服务器上。
• 使用更快的硬件设备,特别是更快的IO设备

• 在索引期间复用单一的IndexWriter实例

• 使用按照内存消耗Flush代替根据文档数量Flush 
在Lucene 2.2之前的版本,可以在每次添加文档后调用ramSizeInBytes方法,当索引消耗过多的内存时,然后在调用flush()方法。这样做在索引大量小文档或者文档大小不定的情况下尤为有效。你必须先把maxBufferedDocs参数设置足够大,以防止writer基于文档数量flush。但是注意,别把这个值设置的太大,否则你将遭遇Lucene-845号BUG。不过这个BUG已经在2.3版本中得到解决。

在Lucene2.3之后的版本。IndexWriter可以自动的根据内存消耗调用flush()。你可以通过writer.setRAMBufferSizeMB()来设置缓存大小。当你打算按照内存大小flush后,确保没有在别的地方设置MaxBufferedDocs值。否则flush条件将变的不确定(谁先符合条件就按照谁)。

• 在你能承受的范围内使用更多的内存 
在flush前使用更多的内存意味着Lucene将在索引时生成更大的segment,也意味着合并次数也随之减少。在Lucene-843中测试,大概48MB内存可能是一个比较合适的值。但是,你的程序可能会是另外一个值。这跟不同的机器也有一定的关系,请自己多加测试,选择一个权衡值。

• 关闭复合文件格式
调用setUseCompoundFile(false)可以关闭复合文件选项。生成复合文件将消耗更多的时间(经过Lucene-888测试,大概会增加7%-33%的时间)。但是请注意,这样做将大大的增加搜索和索引使用的文件句柄的数量。如果合并因子也很大的话,你可能会出现用光文件句柄的情况。

• 重用Document和Field实例 
在lucene 2.3中,新增了一个叫setValue的方法,可以允许你改变字段的值。这样的好处是你可以在整个索引进程中复用一个Filed实例。这将极大的减少GC负担。

最好创建一个单一的Document实例,然后添加你想要的字段到文档中。同时复用添加到文档的Field实例,通用调用相应的SetValue方法改变相应的字段的值。然后重新将Document添加到索引中。

注意:你不能在一个文档中多个字段共用一个Field实例,在文档添加到索引之前,Field的值都不应该改变。也就是说如果你有3个字段,你必须创建3个Field实例,然后再之后的Document添加过程中复用它们。

• 在你的分析器Analyzer中使用一个单一的Token实例 
在分析器中共享一个单一的token实例也将缓解GC的压力。

• 在Token中使用char[]接口来代替String接口来表示数据 
在Lucene 2.3中,Token可以使用char数组来表示他的数据。这样可以避免构建字符串以及GC回收字符串的消耗。通过配合使用单一Token实例和使用char[]接口你可以避免创建新的对象。

• 设置autoCommit为false
在Lucene 2.3中对拥有存储字段和Term向量的文档进行了大量的优化,以节省大索引合并的时间。你可以将单一复用的IndexWriter实例的autoCommit设置为false来见证这些优化带来的好处。注意这样做将导致searcher在IndexWriter关闭之前不会看到任何索引的更新。如果你认为这个对你很重要,你可以继续将autoCommit设置为true,或者周期性的打开和关闭你的writer。

• 如果你要索引很多小文本字段,如果没有特别需求,建议你将这些小文本字段合并为一个大的contents字段,然后只索引contents。(当然你也可以继续存储那些字段)

• 加大mergeFactor合并因子,但不是越大越好 
大的合并因子将延迟segment的合并时间,这样做可以提高索引速度,因为合并是索引很耗时的一个部分。但是,这样做将降低你的搜索速度。同时,你有可能会用光你的文件句柄如果你把合并因子设置的太大。值太大了设置可能降低索引速度,因为这意味着将同时合并更多的segment,将大大的增加硬盘的负担。

• 关闭所有你实际上没有使用的功能 
如果你存储了字段,但是在查询时根本没有用到它们,那么别存储它们。同样Term向量也是如此。如果你索引很多的字段,关闭这些字段的不必要的特性将对索引速度提升产生很大的帮助。

• 使用一个更快的分析器
有时间分析文档将消耗很长的时间。举例来说,StandardAnalyzer就比较耗时,尤其在Lucene 2.3版本之前。你可以尝试使用一个更简单更快但是符合你需求的分析器。

• 加速文档的构建时间 
在通常的情况下,文档的数据来源可能是外部(比如数据库,文件系统,蜘蛛从网站上的抓取等),这些通常都比较耗时,尽量优化获取它们的性能。

• 在你真的需要之前不要随意的优化optimize索引(只有在需要更快的搜索速度的时候)

• 在多线程中共享一个IndexWriter
最新的硬件都是适合高并发的(多核CPU,多通道内存构架等),所以使用多线程添加文档将会带来不小的性能提升。就算是一台很老的机器,并发添加文档都将更好的利用IO和CPU。多测试并发的线程数目,获得一个临界最优值。

• 将文档分组在不同的机器上索引然后再合并
如果你有大量的文本文档需要索引,你可以把你的文档分为若干组,在若干台机器上分别索引不同的组,然后利用writer.addIndexesNoOptimize来将它们合并到最终的一个索引文件中。

• 运行性能测试程序
如果以上的建议都没有发生效果。建议你运行下性能检测程序。找出你的程序中哪个部分比较耗时。这通常会给你想不到的惊喜。


本翻译属于原创,转载时请注明出处,英文原版请查看:

http://wiki.apache.org/jakarta-lucene/ImproveIndexingSpeed


转自:

http://blog.csdn.net/gaoweipeng/archive/2009/10/16/4684198.aspx

 

 

posted on 2010-10-16 14:25  huangfox  阅读(6013)  评论(0编辑  收藏  举报