在有关Lucene的问题(7),讨论了使用Lucene内存索引和硬盘索引构建实时索引的问题。
然而有的读者提到,如果涉及到文档的删除及更新,那么如何构建实时的索引呢?本节来讨论这个问题。
1、Lucene删除文档的几种方式
- IndexReader.deleteDocument(int docID)是用 IndexReader 按文档号删除。
- IndexReader.deleteDocuments(Term term)是用 IndexReader 删除包含此词(Term)的文档。
- IndexWriter.deleteDocuments(Term term)是用 IndexWriter 删除包含此词(Term)的文档。
- IndexWriter.deleteDocuments(Term[] terms)是用 IndexWriter 删除包含这些词(Term)的文档。
- IndexWriter.deleteDocuments(Query query)是用 IndexWriter 删除能满足此查询(Query)的文档。
- IndexWriter.deleteDocuments(Query[] queries)是用 IndexWriter 删除能满足这些查询(Query)的文档。
删除文档既可以用reader进行删除,也可以用writer进行删除,不同的是,reader进行删除后,此reader马上能够生效,而用writer删除后,会被缓存,只有写入到索引文件中,当reader再次打开的时候,才能够看到。
2、Lucene文档更新的几个问题
2.1、使用IndexReader还是IndexWriter进行删除
既然IndexReader和IndexWriter都能够进行文档删除,那么到底是应该用哪个来进行删除呢?
本文的建议是,用IndexWriter来进行删除。
因为用IndexReader可能存在以下的问题:
(1) 当有一个IndexWriter打开的时候,IndexReader的删除操作是不能够进行的,否则会报LockObtainFailedException
(2) 当IndexReader被多个线程使用的时候,一个线程用其进行删除,会使得另一个线程看到的索引有所改变,使得另一个线程的结果带有不确定性。
(3) 对于更新操作,在Lucene中是先删除,再添加的,然而删除的被立刻看到的,而添加却不能够立刻看到,造成了数据的不一致性。
(4) 即便以上问题可以通过锁来解决,然而背后的操作影响到了搜索的速度,是我们不想看到的。
2.2、如何在内存中缓存文档的删除
在上一节中,为了能够做到实时性,我们使用内存中的索引,而硬盘上的索引则不经常打开,即便打开也在背后线程中打开。
而要删除的文档如果在硬盘索引中,如果不重新打开则看不到新的删除,则需要将删除的文档缓存到内存中。
那如何将缓存在内存中的文档删除在不重新打开IndexReader的情况下应用于硬盘上的索引呢?
在Lucene中,有一种IndexReader为FilterIndexReader,可以对一个IndexReader进行封装,我们可以实现一个自己的FilterIndexReader来过滤掉删除的文档。
一个例子如下:
public class MyFilterIndexReader extends FilterIndexReader { OpenBitSet dels; public MyFilterIndexReader(IndexReader in) { super(in); dels = new OpenBitSet(in.maxDoc()); } public MyFilterIndexReader(IndexReader in, List<String> idToDelete) throws IOException { super(in); dels = new OpenBitSet(in.maxDoc()); for(String id : idToDelete){ TermDocs td = in.termDocs(new Term("id", id)); //如果能在内存中Cache从Lucene的ID到应用的ID的映射,Reader的生成将快得多。 if(td.next()){ dels.set(td.doc()); } } } @Override public int numDocs() { return in.numDocs() - (int) dels.cardinality(); } @Override public TermDocs termDocs(Term term) throws IOException { return new FilterTermDocs(in.termDocs(term)) { @Override public boolean next() throws IOException { boolean res; while ((res = super.next())) { if (!dels.get(doc())) { break; } } return res; } }; } @Override public TermDocs termDocs() throws IOException { return new FilterTermDocs(in.termDocs()) { @Override public boolean next() throws IOException { boolean res; while ((res = super.next())) { if (!dels.get(doc())) { break; } } return res; } }; } } |
2.3、文档更新的顺序性问题
Lucene的文档更新其实是删除旧的文档,然后添加新的文档。如上所述,删除的文档是缓存在内存中的,并通过FilterIndexReader应用于硬盘上的索引,然而新的文档也是以相同的id加入到索引中去的,这就需要保证缓存的删除不会将新的文档也过滤掉,将缓存的删除合并到索引中的时候不会将新的文档也删除掉。
Lucene的两次更新一定要后一次覆盖前一次,而不能让前一次覆盖后一次。
所以内存中已经硬盘中的多个索引是要被保持一个顺序的,哪个是老的索引,哪个是新的索引,缓存的删除自然是应该应用于所有比他老的索引的,而不应该应用于他自己以及比他新的索引。
3、具有更新功能的Lucene实时索引方案
3.1、初始化
首先假设我们硬盘上已经有一个索引FileSystemIndex,被事先打开的,其中包含文档1,2,3,4,5,6。
我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打开,其中包含文档7,8。
3.2、更新文档5
这时候来一个新的更新文档5, 需要首先将文档5删除,然后加入新的文档5。
需要做的事情是:
- 首先在内存索引中删除文档5,当然没有文档5,删除无效。
- 其次将对文档5的删除放入内存文档删除列表,并与硬盘的IndexReader组成FilterIndexReader
- 最后,将新的文档5加入内存索引,这时候,用户可以看到的就是新的文档5了。
- 将文档5放入删除列表以及将文档5提交到内存索引两者应该是一个原子操作,好在这两者都是比较块的。
注:此处对硬盘上的索引,也可以进行对文档5的删除,由于IndexReader没有重新打开,此删除是删不掉的,我们之所以没有这样做,是想保持此次更新要么全部在内存中,要么全部在硬盘中,而非删除部分已经应用到硬盘中,而新文档却在内存中,此时,如果系统crash,则新的文档5丢失了,而旧的文档5也已经在硬盘上被删除。我们将硬盘上对文档5的删除放到从内存索引向硬盘索引的合并过程。
如果再有一次对文档5的更新,则首先将内存索引中的文档5删除,添加新的文档5,然后将文档5加入删除列表,发现已经存在,则不必删除。
3.3、合并索引
然而经过一段时间,内存中的索引需要合并到硬盘上。
在合并的过程中,需要重新建立一个空的内存索引,用于合并阶段索引新的文档,而合并中的索引的IndexReader以及硬盘索引和删除列表所组成的FilterIndexReader仍然保持打开,对外提供服务,而合并阶段从后台进行。
后台的合并包括以下几步:
- 将删除列表应用到硬盘索引中。
- 将内存索引合并到硬盘索引中。
- IndexWriter提交。
3.4、合并的过程中更新文档5
在合并的过程中,如果还有更新那怎么办呢?
- 首先将合并中索引的文档5删除,此删除不会影响合并,因为合并之前,合并中索引的IndexReader已经打开,索引合并中索引的文档5还是会合并到硬盘中去的。此删除影响的是此后的查询在合并中索引是看不到文档5的。
- 然后将文档5的删除放入删除列表,并同合并中索引的删除列表,已经硬盘索引一起构成FilterIndexReader。
- 将新的文档5添加到内存中索引。
- 提交在合并中索引对文档5的删除,将文档5添加到删除列表,提交在内存索引中对文档5的添加三者应该是一个原子操作,好在三者也是很快的。
3.5、重新打开硬盘索引的IndexReader
当合并中索引合并到硬盘中的时候,是时候重新打开硬盘上的索引了,新打开的IndexReader是可以看到文档5的删除的。
如果这个时候有新的更新,也是添加到内存索引和删除列表的,比如我们更新文档6.
3.6、替代IndexReader
当IndexReader被重新打开后,则需要删除合并中的索引及其删除列表,将硬盘索引原来的IndexReader关闭,使用新的IndexReader。
http://www.360doc.com/content/15/0817/00/18167315_492203143.shtml
相关推荐
在构建实时索引时,尤其是在处理文档的更新和删除时,需要理解Lucene提供的不同方法以及它们的适用场景。以下是对Lucene删除文档和更新文档的详细讨论。 1. Lucene删除文档的方式: - `IndexReader.deleteDocument...
然而,传统的Lucene在处理大规模数据时,由于其索引构建的离线特性,往往无法满足实时或近实时的索引更新需求。为了解决这个问题,Zoie应运而生。本篇文章将详细介绍如何利用Lucene 5和Zoie来构建一个能够实现实时或...
本篇文章将详细阐述如何使用Lucene来创建和查询索引,帮助你深入理解其核心概念和操作流程。 ### 1. Lucene基本概念 - **文档(Document)**:在Lucene中,一个文档代表你要索引的信息单元,它可以包含多个字段...
- 使用Lucene提供的API来创建索引。 - 需要创建一个`IndexWriter`对象,指定索引存储的位置及索引的配置选项。 - 对于每个XML文档中的元素,可以创建一个`Document`对象,并将其添加到`IndexWriter`中。 3. **...
以上就是Lucene索引器实例的基本介绍,通过这个实例,我们可以理解到如何使用Lucene来创建、管理和搜索文本索引。在实际项目中,可以根据需求选择合适的存储(如硬盘目录或分布式存储)、优化分析器配置、处理大量...
在创建索引时,Lucene会对文档进行分词,生成一系列的关键词(也称为术语或Token),然后构建倒排索引。倒排索引是一种数据结构,它将每个关键词与包含该关键词的文档位置相关联,使得我们可以迅速找到包含特定词汇...
《深入 Lucene 索引机制》这篇博文主要探讨了Lucene这个全文搜索引擎的核心索引原理,它在信息检索领域有着广泛的应用。Lucene是一个开源的Java库,它提供了高效、可扩展的文本搜索功能。以下是对Lucene索引机制的...
通过上述步骤,我们可以构建一个基于Lucene的增量索引系统,高效地处理数据变化,同时保持搜索效率。在实际应用中,还需要结合具体的业务需求和数据特性进行定制化开发。通过持续监控和优化,我们可以确保系统的稳定...
在使用 Lucene 进行信息检索时,有时我们需要对建立的索引进行查看、调试或分析,这时就需要借助 Lucene 的索引查看工具。 Luke 是一个非常实用的 Lucene 索引浏览器,全称为 Lucidworks Luke。它允许用户以图形化...
Lucene最初是用Java编写的,因此在Java环境中使用非常方便。它提供了丰富的API,包括文档(Document)、字段(Field)、索引编写器(IndexWriter)、索引阅读器(IndexReader)、查询解析器(QueryParser)等类,...
通过阅读和分析源代码,我们可以学习到如何操作Lucene索引,以及如何构建类似的工具。 总结而言,luke作为Lucene索引的可视化工具,极大地便利了开发者对索引的理解和调试。无论是初学者还是经验丰富的开发人员,都...
因此,在构建索引之前,需要先使用合适的分词器对文本进行分词处理,常见的有IKAnalyzer、SmartCN等。选择合适的分词器对于提高检索质量至关重要。 2. **停用词处理**:在中文文本中存在大量无实际意义的词汇(如...
《Lucene全文检索:简单索引与搜索实例详解》 Lucene是Apache软件基金会的开源项目,是一款强大的全文检索库,被广泛应用于Java开发中,为开发者提供了构建高性能搜索引擎的能力。在本文中,我们将深入探讨如何基于...
Lucene本身并不直接支持增量索引,因此,开发者们开发了Zoie系统,它是基于Lucene的一个扩展,旨在解决大数据量场景下的实时索引问题。Zoie的名字来源于“Zero Indexing Overhead”,即零索引开销,它的核心思想是...
2. **创建索引**: 使用Lucene.Net的`Directory`类(如`FSDirectory`)创建一个目录来存储索引。然后,通过`Analyzer`接口实现分词器。在这个例子中,我们将使用盘古分词的`PanguAnalyzer`类。创建一个`IndexWriter`...
通过对“lucene_multiThreadIndex”压缩包的学习,你将掌握如何在Lucene中实现多线程索引,从而提高大型数据集的索引构建速度。通过实践,你可以更好地理解和应用这些技术,优化你的信息检索系统。
本篇文章将详细介绍如何使用Lucene3.0来创建索引,并通过一个具体的例子来演示整个过程。 #### 一、Lucene3.0简介 Lucene是一款高性能、全功能的全文搜索引擎库。它为开发者提供了构建搜索应用所需的所有基本工具...
在实际开发中,Luke是Lucene开发者的得力助手,它能帮助快速定位问题,验证索引构建是否正确,或者对搜索结果进行调试。使用Luke 7.4.0,你可以深入了解Lucene 7.x版本的索引结构和工作原理,从而提高你的搜索引擎...
在深入探讨Lucene删除索引这一主题之前,我们先来理解一下Lucene的基本概念。Lucene是一个开源的全文搜索引擎库,由Apache软件基金会开发。它提供了高性能、可扩展的搜索和索引功能,广泛应用于各种应用程序中,如...
假设有一家互联网公司正在开发一个新的搜索引擎产品,他们选择使用Lucene作为核心的索引构建技术。为了应对每天新增的大量网页数据,该公司决定采用基于Lucene的分布式并行索引技术。具体步骤如下: 1. **需求分析*...