写文档时, 偶然发现很久以前的东西,给大家分享一下. 现在lucene已经过了这个版本, 已经有OpenBitSet这种好东西了.~
1. lucene2.3.2的变更
2. 疱丁解牛分词器分析
3. 几种分词器精度和速度的比较
Lucene2.3.2的变更
Author: Jeremy Chow([email protected])
Last Modified: Aug 1st, 2008
一、运行时
1. IndexWriter最大化地提高了现有的索引速度。第一,IndexWriter现在由内存的使用情况来决定刷新索引至磁盘,而不是之前用IndexWriter.setMaxBufferedDocs规定缓存的Docuement数量来决定刷新行为。第二,ConcurrentMergeScheduler使用后方(backgroud)线程(调用IndexWriter.setMergeScheduler (new SerialMergeScheduler())保持向下兼容) 来进行合并操作,由此得到的好处是合并行为不会阻塞IndexWriter.addDocument操作。第三,现在对segment的合并操作是依据每个segment大小,而不再是依据它们所包含Document的数量。
2. SortField.AUTO对long型起作用。在之前的lucene版本中,为排序自动检测Field类型时,数字首先被解释成int,如果解释失败,则认为是float。现在则先检测是否int,然后是否long,最后是float。
二、API 的变更
1. 新加了IndexWriter.setRAMBufferSizeMB(...),IndexWriter当缓存的Documents容量大于指定内存大小时进行刷新。同时,Token也添加了新的API,使用户使用char[]结合偏移量与长度来表示token (避免为每个Token调用一次new String ())。
三、新特性
1. 新加了IndexReader.reopen() 方法用来重新打开已经存在的IndexReader。
2. 新加了IndexWriter.optimize(int maxNumSegments) 用来局部优化值小于maxNumSegments 的片段
3. 新加了IndexWriter.optimize(int maxNumSegments) 局部优化索引
4. 新加了IndexCommit.isOptimized()。
5. 新加了TokenFilter.reset()。
四、优化
1. CachingTokenFilter 现在使用一个迭代器来访问缓存在LinkedList内的Tokens。显著地提升了性能,特别是在Tokens数目比较大的情况。
2. 从实质上优化了IndexWriter怎么使用RAM来缓存Documents,使索引速度加快到之前的2~8倍。现在使用了一个简单的共享散列表记录在内存中的Postings。一个Term对应一个posting。这个表直接写入到一个segment中。
3. 消除了偶尔会发生在复合文件(compound files)上的缓存到缓存拷贝(buffer to buffer copy)。
4. 去掉了Document中的同步机制,实际就是把存储Fields的Vector改成了ArrayList。
5. StandardTokenizer (StandardAnalyzer)使用JFlex代替JavaCC生成Tokenizer,使其速度是以前的6倍。
6. 使用bulk-coping技术拷贝原始字节,加速邻接的未删除文档的合并。
7.加速了 ISOLatin1AccentFilter 。
五、重新打开索引文件
IndexReader .reopen()
如果IndexReader实例调用此方法时,索引有变化,则生成一个新的IndexReader实例。
打开索引是非常昂贵的操作。此方法可以用来刷新已经存在的IndexReader,减少代价。此方法尝试只加载已经变化或新建立的片段(segement)。
如果索引自从打开后没有变化,则返回原来的IndexReader对象,否则会返回一个新的IndexReader对象。此时旧的IndexReader对象没有 关闭,只是不可用。
注意: 新打开的IndexReader对象可能会与旧的对象共享资源,据此,千万不要调用这些对象对索引的任何修改操作(例如:deleteDocument(int) , setNorm(int, String, byte) ),除非旧IndexReader对象已经关闭。否则,其行为将是不可确定的。
你可以通过比较返回的IndexReader与原来的IndexReader来判断其是否真的重新打开了:
IndexReader reader = ...
...
IndexReader new = r.reopen();
if (new != reader) {
... // reader was reopened
reader.close();
}
reader = new;
...
六、建立索引优化原理
以DocumentsWriter代替了之前的DocumentWriter。前者接受多个文档(Documents)的添加,并且直接把它们写入到一个单一的片段(segment)中。它比lucene2.2.0中用DocumentWriter为每个文档建立一个片段,然后将这些片段合并的方法更高效。
当加一篇文档时,它所存储的属性(Field)和词条向量(Term Vector)立即被写入到磁盘目录(假设不用RAM)。词频freq/prox 等词条信息(Posting),以词条为键值加入到Posting散列中。Posting散列表的每项维护着一个词频和prox组成的独立字节流。它包含多个文档的Posting数据。如果启用了向量,则为每个文档的每条词条都分配一个Posting向量(Posting Vector)。向量的每个元素记录词条的偏移量、位置等信息。
当Posting散列已满(假设使用RAM)或者加入到RAM的文档数目足够大(在此,我们用文档数目,而不是RAM使用量来刷新索引)时,我们会建立一个真实的片段,把它刷新到磁盘,然后重置Posting散列。
加入文档时,我们首先由属性(Field)名来组织文档所有的属性,并为每个属性记录其Posting散列。处理完每个属性后,我们刷新它的词条向量。当到整个片段的时候,我们首先通过名称将属性(fields)排序,然后依次排序每个属性的Postings。
线程:
多条线程可以同时调用addDocument方法。DocumentsWriter内部有一个getThreadState的同步调用,它为当前线程式分配一个ThreadState对象。无论何时,同一线程将会得到同一ThreadState。因此,如果各线程为各自不同的内容建立索引,则可以更有效地使用RAM。之后,processDocument会在那个ThreadState之上被调用,它不用作同步(很多消耗在这一步)。这是lucene2.3.0最重要的改进。 最后,同步调用finishDocument刷新变化至磁盘目录。
每个ThreadState实例都有属于其自身的Postng散列。当我们使用过多RAM时,会通过合并多个线程状态(ThreadSate)中相同词条的Posting表中的文档ID(docIDs),刷新所有线程的Posting散列至一个片段中。
当IndexWriter调用刷新操作(flush),且autoCommit=false时,会执行刷新。此时,我们强制所有线程空闲,且确定它们都空闲后才刷新。这意味着,你可以由一个给定的线程调用刷新操作。
七、建索引速度测试
CPU: Intel Dual CPU 2.00GHz,
内存: 1G DDR400
文本材料大小 : : 43.9 MB
文件数目 : 19997 个
分析器 : lucene 标准 StandardAnalyzer
Lucene 版本: 2.2.0
生成索引大小 : : 16,901,638 字节
耗时:
158094 微秒
110437 微秒
106328 微秒
Lucene 版本: 2.3.2
生成索引大小 : 16,177,772 字节
默认 16M 缓存耗时 :
15407 微秒
15500 微秒
设置 64M 缓存耗时 :
13578 微秒
13984 微秒
13359 微秒
15500 微秒
可以看出 2.3 比 2.2 建索引的速度要快 5~10 倍。 (不过现在看来是因为StandardAnalyzer变快了. )