搜索引擎Lucene(4):索引的创建过程

搜索引擎Lucene(4):索引的创建过程,第1张

创建索贺稿引的过程如下:

索引结构如下:

IndexWriter结构:

IndexWriter通过指定存放的目录(Directory)以及文档分析器(Analyzer)来构建,direcotry代表索引存储在哪里;analyzer表示如何来分析文档的内容;similarity用来规格化文档,给文档算分;IndexWriter类里还有一些SegmentInfos对象用于存储索引片段信息,以及发生故障回滚等。

添加文档使用addDocument()方法,删除文档使用deleteDocuments(Term)或者deleteDocuments(Query)方法,而且一篇文档可以使用updateDocument()方法来更新(仅仅是先执行delete在执行add *** 作而已)。当完成了添加、删除、更新文档,应该需要调用close方法。

这些修改会缓存在内存中(buffered in memory),并且定期地(periodically)刷新到(flush)Directory中(在上述方法的调用期间)。一次flush *** 作会在如下时候触发(triggered):当从上一次flush *** 作后有禅培孝足够多缓存的delete *** 作(参见setMaxBufferedDeleteTerms(int)),或者足够多已添加的文档(参见setMaxBufferedDocs(int)),无论哪个更快些(whichever is sooner)。对被添加的文档来说,一次flush会在如下任何一种情况下触发,文档的RAM缓存使用率(setRAMBufferSizeMB)或者已添加的文档数目,缺省的RAM最高使用率是16M,为得到索引的最高效率,你需要使用更大的RAM缓存大小。需要注意的是,flush处理仅仅是将IndexWriter中内部缓存的状态(internal buffered state)移动进索引里去,但是这些改变不会让IndexReader见到,直到commit()和close()中的任何一个方法被调用时。一次flush可能触发一个或更多的片断合并(segmentmerges),这时会启动一个后台的线程来处理,所以不会中断addDocument的调用,请参考MergeScheduler。

一个IndexReader或者IndexSearcher只会看到索引在它打开的当时的状态。任何在索引被打开之后提交到索引中的commit信息,在它被重新打开之前都不会见到。

DocumentsWriter结构:

DocumentsWriter 是由IndexWriter 调用来负责处理多个文档的类,它通过与Directory 类及Analyzer 类、Scorer 类等将文档内容提取出来,并分解成一组term列表再生成一个单一的segment 所需要的数据文件,如term频率、term 位置、term 向量等索引文件,以便SegmentMerger 将它合并到统一的segment 中去。

该类可接收多个添加的文档,并且直接写成一个单独的segment 文件。这比为每一个文档创建一个segment(使用DocumentWriter)以及对那些segments 执行合中手作处理更有效率。

每一个添加的文档都被传递给DocConsumer类,它处理该文档并且与索引链表中(indexing chain)其它的consumers相互发生作用(interacts with)。确定的consumers,就像StoredFieldWriter和TermVectorsTermsWriter,提取一个文档的摘要(digest),并且马上把字节写入“文档存储”文件(比如它们不为每一个文档消耗(consume)内存RAM,除了当它们正在处理文档的时候)。

其它的consumers,比如FreqProxTermsWriter和NormsWriter,会缓存字节在内存中,只有当一个新的segment制造出的时候才会flush到磁盘中。

一旦使用完我们分配的RAM缓存,或者已添加的文档数目足够多的时候(这时候是根据添加的文档数目而不是RAM的使用率来确定是否flush),我们将创建一个真实的segment,并将它写入Directory中去。

索引创建的调用过程:

一个Directory对象是一系列统一的文件列表(a flat list of files)。文件可以在它们被创建的时候一次写入,一旦文件被创建,它再次打开后只能用于读取(read)或者删除(delete) *** 作。并且同时在读取和写入的时候允许随机访问。

FSDirectory类直接实现Directory抽象类为一个包含文件的目录。目录锁的实现使用缺省的SimpleFSLockFactory,但是可以通过两种方式修改,即给getLockFactory()传入一个LockFactory实例,或者通过调用setLockFactory()方法明确制定LockFactory类。

目录将被缓存(cache)起来,对一个指定的符合规定的路径(canonical path)来说,同样的FSDirectory实例通常通过getDirectory()方法返回。这使得同步机制(synchronization)能对目录起作用。

RAMDirectory类是一个驻留内存的(memory-resident)Directory抽象类的实现。目录锁的实现使用缺省的SingleInstanceLockFactory,但是可以通过setLockFactory()方法修改。

IndexInput类是一个为了从一个目录(Directory)中读取文件的抽象基类,是一个随机访问(random-access)的输入流(input stream),用于所有Lucene读取Index的 *** 作。BufferedIndexInput是一个实现了带缓冲的IndexInput的基础实现。

IndexOutput类是一个为了写入文件到一个目录(Directory)中的抽象基类,是一个随机访问(random-access)的输出流(output stream),用于所有Lucene写入Index的 *** 作。BufferedIndexOutput是一个实现了带缓冲的IndexOutput的基础实现。RAMOuputStream是一个内存驻留(memory-resident)的IndexOutput的实现类。

域索引选项通过倒排索引来控制文本是否可被搜索。

当lucene建立起倒排索引后,默认情况下它会保存所有必要的信息以实施Vector Space Model。该Model需要计算文档中出现的Term数,以及它们出现的文职(这是必要的,比如通过词组搜索时用到)。但有时候这些域只是在布尔搜索时用到,他们并不为相关评分做贡献,一个常见的例子是,域只是被用作过滤,如权限过滤和日期过滤。在这种情况下,可以通过调用Field.setOmitTermFreqAndPositions(true)方法让lucene跳过对改选项的出现频率和出现位置的索引。该方法可以节省一些索引在磁盘上的储存空间,还可以加速搜索和过滤过程,但会悄悄阻止需要位置信息的搜索,如阻止PhraseQuery和SpanQuery类的运行。

域存储选项是用来确定是否需要存储域的真实值,以便后续搜索时能回复这个值。

lucene支持想一个域中写入多个不同的值。

这种处理方式是完全可以接受并鼓励使用的,因为这是逻辑上具有多个域值的域的自然表示方式。在lucene内部,只要文档中出现同名的多域值,倒排索引和项向量都会在逻辑上将这些语汇单元附加进去,具体顺序由添加该域的顺序决定。

文档和域的加权 *** 作可以在索引期间完成。而搜索期间的加权 *** 作会更加动态化,因为每次搜索都可以根据不同的加权因子独立选择加权或不加权,但这个策略也可能多消耗一些CPU效率。搜索期间的动态加权可以更灵活控制。

默认情况下,所有文档的加权因子都是1.0,通过改变文档的加权因子,就可以影响文档在索引中的重要程度。调整加权 *** 作的API为:setBoost(float);

同文档加权一样,可以对进行加权 *** 作。文档加权时,lucene内部会采用同一加权因子来对该文档中的域进行加权。域加权API:Field.setBoost(fliat)。

Analyzer类构建用于分析文本的TokenStream对象,因此(thus)它表示(represent)用于从文本中分解(extract)出组成索引的terms的一个规则器(policy)。典型的(typical)实现首先创建一个Tokenizer,它将那些从Reader对象中读取字符流(stream of characters)打碎为(break into)原始的Tokens(raw Tokens)。然后一个或更多的TokenFilters可以应用在这个Tokenizer的输出上。警告:你必须在你的子类(subclass)中覆写(override)定义在这个类中的其中一个方法,否则的话Analyzer将会进入一个无限循环(infinite loop)中。

StandardAnalyzer:

StandardAnalyzer类是使用一个English的stop words列表来进行tokenize分解出文本中word,使用StandardTokenizer类分解词,再加上StandardFilter以及LowerCaseFilter以及StopFilter这些过滤器进行处理的这样一个Analyzer类的实现。

//创建索桐敏引器对象,追加的方式创建索引

IndexWriter index = new IndexWriter(this.INDEX_PATH, new IKAnalyzer(), false, IndexWriter.MaxFieldLength.UNLIMITED)

index.setMaxFieldLength(1000000)

index.setMergeFactor(100)

Document doc = new Document()

Field field = new Field("name", "value", Field.Store.YES, Field.Index.ANALYZED)

doc.add(field)

index.addDocument(doc)

index.commit()

index.optimize()

index.close()

这代码网上皮简一搜一大堆,这段代码是lucene2.4的代码,估计2.0+的版本基本上这个写都没错燃轮裤,但是lucene3.0+版本稍微有些改动,不知道你用的什么版本

磁盘空间大小,这个直接影响索引的建立,甚至会造成索引写衡滑入提示完成,但是没有同步的问题;

索引合并策略的选择,这个类似于sql里边的批量 *** 作,批量 *** 作的祥闭数量过多直接影响执行效率,对于lucene来讲,索引合并前是将document放在内存中,因此选择合适的合并策略也可以提升索引的效率;

唯一索引对应的term的选择,lucene索引的创建过程中是先从索引中删除包含相同term的document然后咐宴腊重新添加document到索引中,这里如果term对应的document过多,会占用磁盘IO,同时造成IndexWriter的写锁占用时间延长,相应的执行效率低下;


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12507911.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-26
下一篇 2023-05-26

发表评论

登录后才能评论

评论列表(0条)

保存