Lucene入门之索引的建立和优化

时间:2022-05-27 16:58:20

索引的建立和优化

索引的建立

   对不同的文本使用不同的分析器

     普通情况下,建立索引器,并向索引器添加文档的语句如下:

     IndexWriter writer=new IndexWriter(indexPath,new StandardAnalyzer());

     Writer.addDocument(doc);

     这种情况下,所有的文档都会被同一种分析器所处理。我们要处理的文档中即包含中文,也包含法文,或者其他的语言的时候,如果只按一种分析器去处理,那么就会导致一种文档分析得好,另一种文档分析得差。

     为了解决这个问题,就可以使用下面的语句:

     Writer.addDocument(doc, 分析器实例 );

     通过这个方法,可以对不同的文本使用不同的分析器。

   Lucene 内置的分析器

     Lucene 使用 JavaCC 来生成分析器。

     Lucene 内置了一些分析器。

分析器类型

分析器原理简述

WhitespaceAnalyzer

在空格处进行词语的切分

SimpleAnalyzer

在非字母字符处切分文本,并转化成小写形式

StopAnalyzer

在非字母字符处切分文本,然后小写化,再移除忽略词

StandardAnalyzer

基于某种语法规则将文本切分成词语快

     Lucene 还内置了很多其他类型的分析器。还有很多有志之士贡献了自己开发的分析器。

   开发自己的分析器

      这里只说开发的具体思路,不谈分析器的具体思路,我也不太会。

1,   继承 Analyzer, 覆盖 tokenStream 的方法

package org.apache.lucene.analysis;

import java.io.Reader;

public class MyAnalyzer extends Analyzer

{

  public TokenStream tokenStream(String fieldName, Reader reader)

  {

      return new MyTokenizer(reader);

  }

}

 

2,   实现自己的 Tokenizer

    

import org.apache.lucene.analysis.Token;

import org.apache.lucene.analysis.Tokenizer;

public class MyTokenizer extends Tokenizer

{

    public MyTokenizer(Reader reader)

    {

    }

    public Token next()

    {

    }

}

 

   根据不同的 Field 使用不同的分析器

      如果一篇中文的论文,里面有一段英文的摘要,我们要对摘要信息进行索引,应该怎么办呢? Lucene 中的 PerFieldAnalyzerWrapper 可以解决这个问题。

      这个类继承自 Analyzer ,使用 addAnalyzer( 字段名称,分析器实例 );

      例如:

PerFieldAnalyzerWrapper wr = new PerFieldAnalyzerWrapper(new StandardAnalyzer());

wr.addAnalyzer("title", new MMAnalyzer());

wr.addAnalyzer("author", new MMAnalyzer());

wr.addAnalyzer("abstract", new StandardAnalyzer());

wr.addAnalyzer("time", new StandardAnalyzer());

索引文件的生成

生成的过程

    IndexWriter 调用 DocumentWriter,DocumentWriter 能获得 Field 的信息,建立倒排索引,与文件系统交互。

索引文件的格式

文件类型

存储含义

Segments

索引块

Fnm

Field 的名称

Fdt

存储了所有设有 Store.YES Field 的数据

Fdx

存储文档在 fdt 中的位置

Cfs

复合式索引格式的索引文件

索引的优化

优化的原则

1,   利用缓存,建设磁盘的读写频率。

2,   简述索引文件的大小和数量。

复合式索引格式

IndexWriter 具有 setUseCompoundFile 方法,通过这个方法可以设置是否使用复合式索引。默认值是 True

使用复合式索引可以减少索引文件的数量。

调整索引优化参数

第一个优化的参数 mergeFactor

用来控制索引块的合并频率和大小,默认值是 10 。这个参数控制在内存中存储的 Document 对象的数量以及合并多个索引块的频率。在将它们作为单个块写入磁盘之前, Lucene 在内存中默认存储 10 Document 对象。当 mergeFactor 的值为 10 也意味着磁盘上的块数达到 10 的乘方的时候, Lucene 会将这些块合并为一个段。

     也就是说,每当向索引增加 10 Document 的时候,就会有一个索引块被建立起来。当磁盘上有 10 个索引块的时候,将被合并成一个大块。这个大块含有 100 Document. 然后,继续积累,当到 10 个大块的时候,将被合并成一个更大的索引块。这个索引块中有 1000 个索引。

但是这个参数,受到 maxMergeDocs 的制约。由此导致每个索引块中含有的 Document 数量都不可以大于 maxMergeDocs 的值。

使用较大参数的 mergeFactor 会让 Lucene 使用更多的内存,同时使得磁盘写入数据的频率降低,因此加速了索引的过程。但是,大的 mergeFactor 意味着低频率的合并,回导致索引中的索引文件数增多。这样会降低搜索速度。

使用较小参数的 mergeFactor 会减少内存的消耗,并使索引更新的频率升高。这样做使得数据的实时性更强,但是也降低了索引过程的速度。

IndexWriter 类的 setMergeFactor(int mergeFactor) 方法来设置 mergeFactor 的大小。

第二个优化的参数 maxMergeDocs

用来限制每个索引块的文档数量,默认值是 Integer.MAX_VALUE;

较大的 maxMergeDocs 可以加快索引的速度,但是更耗内存。

较大的 maxMergeDocs 参数适用于批量索引的情况,较小的 maxMergeDocs 参数适用于交互性较强的索引。

IndexWriter.setMaxMergeDocs(int maxMergeDocs) 方法来设置 maxMergeDocs 的大小。

第三个优化的参数 maxBuffer Docs

这个参数用于控制内存中文档的数量。 默认值是 10.

这个值越大,在内存中存储文档就越多,越消耗内存,同时磁盘的 I/O 越少。

这些参数都是针对 Lucene2.1 而言的,好像 Lucene2.4 有了变化。

内存缓冲器与索引合并

   为了将索引放在内存中缓冲起来,我们需要内存缓冲器。

   Public IndexWriter(Directory d,Analyzer a,Boolean create) throws IOException

   Directory 分为两种,一种是 RAMDirectory 或者 FSDirectory 得实例。

   使用 RAMDirectoy ,就是在内存中建立索引。这种索引建立的方式特别快,但是不能永久保存。

   使用 FSDirectory ,就是在磁盘中建立索引。建立起来的索引能永久保存,但是索引速度慢。

   如果把 RAMDirectory 作为缓冲器,先将索引文件缓存在缓冲器中,再把数据写入基于 FSDirectory 的索引中,从而达到改善性能的目的。

   RAMDirectory 的使用方法

  

// 创建缓冲器对象

RAMDirectory rd = new RAMDirectory();

// 创建索引器

IndexWriter writer = new IndexWriter(rd,new StandardAnalyzer());

// 创建搜索器

IndexSearcher searcher = new IndexSearcher(rd);

 

   FSDirectory 的使用方法

  

FSDirectory fd = FSDirectory.getDirectory("index");

IndexWriter writer = new IndexWriter(fd,new StandardAnalyzer());

IndexSearcher searcher = new IndexSearcher(fd);

 

   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();

限制每个 Field 的词条数量

     对于 Document 德某个 Field ,我们可以限定它可被拆分的最大的词条数量。

     使用 IndexWriter 如下方法即可:

     Public void setMaxFieldLength(int maxFieldLength);

     如果某个 Field 别拆分了大量的词条,那么将消耗大量的内存。很容易导致内存溢出,这个问题在大文档的情况下尤其容易发生,所以要限定。

     通常, maxFieldLength 不应超过 10000

索引本身的优化

     IndexWriter

     Public void optimize() throws IOException

     这个方法可以提高索引搜索操作的速度,但是不会影响建立的速度。

查看索引的过程

       IndexWriter setInfoStream 可以用来查看索引的过程。

       例如:

PrintStream ps = new PrintStream("log.txt");

writer.setInfoStream(ps);